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
A repo created with is_public = false is still fully enumerable by an unauthenticated caller. is_public gates content reads (blob/tree/clone via visibility_check) but no listing surface honors it, so a private repo's name, owner_did, description, is_public flag, default_branch, and created_at are returned to anyone. This is a metadata/discoverability leak, not a content leak.
Affected surfaces (all unauthenticated)
GET /api/v1/repos (list) — crates/gitlawb-node/src/api/repos.rs:230-246; route mounted open at server.rs:277.
GET /api/v1/repos/federated — repos.rs:1126; route open at server.rs:278.
GET /api/v1/statsrepos count — server.rs:454 via Db::count_repos_deduped; route open at server.rs:400. Counts private repos toward the public total.
GraphQL repos query — crates/gitlawb-node/src/graphql/query.rs:12. /graphql runs queries without auth (server.rs:61-67; auth is attached only when a signature is present, and only mutations enforce it).
Each maps rows through to_response / RepoType, which include name, owner_did, description, is_public, default_branch, created_at.
Verified
visibility_check only consumes is_public for path/content authorization (visibility.rs, api/mod.rs, git/visibility_pack.rs, api/encrypted.rs, api/pulls.rs). No listing query filters on it.
Content is still gated: for is_public = false, no rules, anonymous caller, path /, visibility_check returns Deny (visibility.rs:89-94), so clone/blob/tree of a private repo are blocked. Only metadata leaks.
As an anonymous caller: GET /api/v1/repos, GET /api/v1/stats, or POST { repos { name ownerDid description } } to /graphql.
The private repo's name/owner/description appear in the response and its existence counts toward /api/v1/stats.
Suggested direction
Have the listing surfaces honor is_public for non-owner/anonymous callers: a repo should appear in a listing only when visibility_check(rules, is_public, owner_did, caller, "/") == Allow (the same gate clone uses at root), or, more cheaply for the anonymous case, filter to is_public = true plus repos the authenticated caller owns. The count surface (count_repos_deduped) needs the matching filter so the stat and the list agree. The owner viewing their own repos must still see their private ones.
Open question
Confirm intent: should is_public = false hide a repo from anonymous discovery, or is repo existence intentionally public with privacy enforced only at the content layer? If the latter, this is a documentation fix (state that listings are public by design) rather than a code change. Filing because is_public exists specifically to mark a repo private and no surface currently honors it for discovery.
Summary
A repo created with
is_public = falseis still fully enumerable by an unauthenticated caller.is_publicgates content reads (blob/tree/clone viavisibility_check) but no listing surface honors it, so a private repo's name,owner_did, description,is_publicflag,default_branch, andcreated_atare returned to anyone. This is a metadata/discoverability leak, not a content leak.Affected surfaces (all unauthenticated)
GET /api/v1/repos(list) —crates/gitlawb-node/src/api/repos.rs:230-246; route mounted open atserver.rs:277.GET /api/v1/repos/federated—repos.rs:1126; route open atserver.rs:278.GET /api/v1/statsreposcount —server.rs:454viaDb::count_repos_deduped; route open atserver.rs:400. Counts private repos toward the public total.reposquery —crates/gitlawb-node/src/graphql/query.rs:12./graphqlruns queries without auth (server.rs:61-67; auth is attached only when a signature is present, and only mutations enforce it).Each maps rows through
to_response/RepoType, which includename,owner_did,description,is_public,default_branch,created_at.Verified
visibility_checkonly consumesis_publicfor path/content authorization (visibility.rs,api/mod.rs,git/visibility_pack.rs,api/encrypted.rs,api/pulls.rs). No listing query filters on it.is_public = false, no rules, anonymous caller, path/,visibility_checkreturnsDeny(visibility.rs:89-94), so clone/blob/tree of a private repo are blocked. Only metadata leaks.Reproduce
is_public: false.GET /api/v1/repos,GET /api/v1/stats, or POST{ repos { name ownerDid description } }to/graphql./api/v1/stats.Suggested direction
Have the listing surfaces honor
is_publicfor non-owner/anonymous callers: a repo should appear in a listing only whenvisibility_check(rules, is_public, owner_did, caller, "/") == Allow(the same gate clone uses at root), or, more cheaply for the anonymous case, filter tois_public = trueplus repos the authenticated caller owns. The count surface (count_repos_deduped) needs the matching filter so the stat and the list agree. The owner viewing their own repos must still see their private ones.Open question
Confirm intent: should
is_public = falsehide a repo from anonymous discovery, or is repo existence intentionally public with privacy enforced only at the content layer? If the latter, this is a documentation fix (state that listings are public by design) rather than a code change. Filing becauseis_publicexists specifically to mark a repo private and no surface currently honors it for discovery.