You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Several repo-scoped read handlers verify the repo exists (get_repo) but never run the visibility gate (authorize_repo_read / visibility_check), so they return a private repo's metadata to an anonymous caller. This is the same class as #94 (hooks) and its PR #113 siblings (events, replicas, protected branches); these are the surfaces that batch did not cover. The mutation handlers on the same files do gate correctly; only the reads were missed.
Where (all verified by reading the handler bodies; none take an AuthenticatedDid caller)
crates/gitlawb-node/src/api/issues.rs — list_issues (:90), get_issue (:120), list_issue_comments (:191). Returns the repo's issue titles/bodies/comments from its git store.
crates/gitlawb-node/src/api/labels.rs — list_labels (:74). Returns the repo's label set.
crates/gitlawb-node/src/api/bounties.rs — list_repo_bounties (:114). Returns the repo's bounties (no repo lookup at all).
crates/gitlawb-node/src/api/stars.rs — get_star_status (:78). Existence + star count oracle for a private repo.
All sit on the anonymous read group (optional_signature).
Impact
For any private repo whose owner-DID slug an attacker can guess or enumerate, an unauthenticated request discloses its ref history and pusher identity (certs), issue content, labels, bounties, and existence. certs is the most severe (ref names + commit SHAs + pusher DID, identical to the #114/#116 leak class); the others are lower but still leak private-repo metadata that the read path otherwise withholds.
Fix
Add authorize_repo_read(&state, &owner, &repo, caller, "/").await? to each read handler (deny -> 404 RepoNotFound, existence-hiding), threading the Option<Extension<AuthenticatedDid>> caller, exactly as #113 did for events/replicas/protected. The matching gl/MCP read callers must then sign (the #115 client-signing class).
Found during a read-only egress audit of every repo-data surface.
Summary
Several repo-scoped read handlers verify the repo exists (
get_repo) but never run the visibility gate (authorize_repo_read/visibility_check), so they return a private repo's metadata to an anonymous caller. This is the same class as #94 (hooks) and its PR #113 siblings (events, replicas, protected branches); these are the surfaces that batch did not cover. The mutation handlers on the same files do gate correctly; only the reads were missed.Where (all verified by reading the handler bodies; none take an
AuthenticatedDidcaller)crates/gitlawb-node/src/api/certs.rs—list_certs(:10),get_cert(:42). Returnsref_name,old_sha,new_sha,pusher_did,node_didfor the repo. This is the same private ref metadata as Unauthenticated GET /api/v1/events/ref-updates leaks private-repo ref metadata (REST analog of #112) #114 / git-receive-pack info/refs advertisement skips the visibility gate, leaking private repo ref metadata #116 (branch names, commit SHAs, pusher identity).crates/gitlawb-node/src/api/issues.rs—list_issues(:90),get_issue(:120),list_issue_comments(:191). Returns the repo's issue titles/bodies/comments from its git store.crates/gitlawb-node/src/api/labels.rs—list_labels(:74). Returns the repo's label set.crates/gitlawb-node/src/api/bounties.rs—list_repo_bounties(:114). Returns the repo's bounties (no repo lookup at all).crates/gitlawb-node/src/api/stars.rs—get_star_status(:78). Existence + star count oracle for a private repo.All sit on the anonymous read group (
optional_signature).Impact
For any private repo whose owner-DID slug an attacker can guess or enumerate, an unauthenticated request discloses its ref history and pusher identity (certs), issue content, labels, bounties, and existence.
certsis the most severe (ref names + commit SHAs + pusher DID, identical to the #114/#116 leak class); the others are lower but still leak private-repo metadata that the read path otherwise withholds.Fix
Add
authorize_repo_read(&state, &owner, &repo, caller, "/").await?to each read handler (deny -> 404 RepoNotFound, existence-hiding), threading theOption<Extension<AuthenticatedDid>>caller, exactly as #113 did for events/replicas/protected. The matchinggl/MCP read callers must then sign (the #115 client-signing class).Found during a read-only egress audit of every repo-data surface.