Skip to content

fix(node): scope the refs listing by the canonical repo slug - #388

Open
beardthelion wants to merge 1 commit into
mainfrom
fix/list-refs-canonical-slug
Open

fix(node): scope the refs listing by the canonical repo slug#388
beardthelion wants to merge 1 commit into
mainfrom
fix/list-refs-canonical-slug

Conversation

@beardthelion

@beardthelion beardthelion commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Closes #387.

list_refs built its branch_cids filter from the raw URL path segments while the only production writer stores the canonical normalize_owner_key(owner_did)/name slug. get_repo normalizes the owner before matching, so did:key:zX and bare zX resolve to the same repository and both pass the read gate, but only one of them matches the exact WHERE repo = $1 that follows. The full-DID form came back 200 {"count":0,"refs":[]}.

The fix binds the record authorize_repo_read already returns and builds the filter from it, so the resource that gets queried is the one that was authorized. api/events.rs and this file's own push path already do exactly this, so the three now agree.

Tests

list_refs had no coverage at all before this, which is why the bug survived since the initial release. Two tests, each confirmed to fail without the line it guards rather than assumed to:

  • list_refs_resolves_both_owner_did_forms_to_the_canonical_slug drives both owner forms through build_router and asserts they return the same refs, with a second repository whose refs must not appear. Restoring the raw path slug turns the positive half red; widening the query to match any owner turns the negative half red.
  • list_refs_denies_an_anonymous_caller_on_a_private_repo covers the deny direction. Replacing authorize_repo_read with a bare get_repo lookup turns it red on its own message. Without this test the first one passes with the read gate removed entirely, since every repository it seeds is public.

Full suite 1090 passed, 0 failed. cargo clippy -D warnings clean, cargo fmt --check clean, and cargo +1.91 check --all-targets --locked clean against the pinned MSRV.

Scope

Only list_refs changes. The raw format!("{owner}/{repo}") shape appears in plenty of other handlers, but everywhere else it feeds a RepoNotFound message or a JSON echo field. As a database query key it occurred once, and this is it.

Reader and writer now hold the slug formula in two matching expressions kept in step by a comment. A shared helper would make that hold by construction, which is worth doing across every slug-keyed reader at once rather than piecemeal here.

Summary by CodeRabbit

  • Bug Fixes

    • Repository branch references now resolve consistently whether the owner is provided as a full or shortened DID.
    • Prevented references from being returned for the wrong repository owner.
    • Private repositories no longer reveal branch information or pinned content identifiers to anonymous requests.
  • Tests

    • Added coverage for equivalent owner formats, cross-owner isolation, and private repository access controls.

`list_refs` built its `branch_cids` filter from the raw URL path segments
while the only production writer stores the canonical
`normalize_owner_key(owner_did)/name` slug. `get_repo` resolves both
`did:key:zX` and bare `zX` to the same record, so a request in the full
DID form passed the read gate and then matched zero rows, returning 200
with an empty ref list. A denial shaped like a success is worse than an
error: a caller cannot tell an empty repository from one it queried under
a form the table does not carry.

Bind the record `authorize_repo_read` already returns and build the filter
from it, so the queried resource is the one that was authorized. This is
the existing pattern in `api/events.rs` and in this file's own push path.

Two regressions, each proven to fail without the line it guards. The first
drives both owner-DID forms through `build_router` and asserts they return
the same refs, with a second repo whose refs must not appear; widening the
query to match any owner turns that half red. The second covers the deny
direction, which the endpoint had never had: a private repo answers an
anonymous caller with the same 404 a missing repo gets and puts no CID in
the body. Without it, removing the read gate outright left the suite green.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7fd4d9be-a88f-431b-be42-d57c3e0bbbb9

📥 Commits

Reviewing files that changed from the base of the PR and between bfc44f9 and b9711d8.

📒 Files selected for processing (2)
  • crates/gitlawb-node/src/api/repos.rs
  • crates/gitlawb-node/src/test_support.rs

Included review availability: 4 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


📝 Walkthrough

Walkthrough

list_refs now builds branch-CID slugs from the authorized repository record. Integration tests cover bare and full did:key: owners, owner isolation, and anonymous access to private repositories.

Changes

Repository refs lookup

Layer / File(s) Summary
Canonical refs resolution and validation
crates/gitlawb-node/src/api/repos.rs, crates/gitlawb-node/src/test_support.rs
list_refs uses the normalized owner DID and canonical repository name from authorization. Integration tests verify equivalent bare and full DID requests, exclude another owner’s refs, and return 404 without exposing private repository CIDs to anonymous callers.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to b9711

This localized change scopes reference listing to the authorized canonical repository and includes focused coverage plus clean validation checks; no actionable merge-blocking risk remains.

Suggested reviewers: vasanthdev2004

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: scoping refs listing by the canonical repository slug.
Description check ✅ Passed The description provides the issue link, motivation, implementation details, test coverage, verification results, and scope. It does not reproduce every template heading or checkbox, but it contains t…
Linked Issues check ✅ Passed The changes satisfy issue #387 by using the authorized repository record to build the canonical slug, preserving read authorization, and adding regression tests for full-DID and bare owner forms plus …
Out of Scope Changes check ✅ Passed The production change is limited to list_refs, and the added tests directly cover the reported bug and its authorization boundary. No unrelated code changes are identified.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 …
Full details: Description check

Explanation

The description provides the issue link, motivation, implementation details, test coverage, verification results, and scope. It does not reproduce every template heading or checkbox, but it contains the required information.

Full details: Linked Issues check

Explanation

The changes satisfy issue #387 by using the authorized repository record to build the canonical slug, preserving read authorization, and adding regression tests for full-DID and bare owner forms plus private-repository denial.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 too large.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/list-refs-canonical-slug

Comment @coderabbitai help to get the list of available commands.

@beardthelion beardthelion added crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior labels Aug 30, 2026

@jatmn jatmn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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

Labels

crate:node gitlawb-node — the serving node and REST API kind:bug Defect fix — wrong or unsafe behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GET /repos/{owner}/{repo}/refs returns an empty 200 when the owner is in full did:key form

2 participants