fix(api): don't expose private org membership via public_members (#38145)

This commit is contained in:
bircni
2026-06-22 05:50:02 +02:00
committed by GitHub
parent e5891263f8
commit 685b62c60f
2 changed files with 37 additions and 0 deletions
+11
View File
@@ -119,6 +119,12 @@ func ListPublicMembers(ctx *context.APIContext) {
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
// don't disclose membership of organizations the doer cannot see
if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
ctx.APIErrorNotFound()
return
}
listMembers(ctx, false) listMembers(ctx, false)
} }
@@ -201,6 +207,11 @@ func IsPublicMember(ctx *context.APIContext) {
if ctx.Written() { if ctx.Written() {
return return
} }
// don't disclose membership of organizations the doer cannot see
if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
ctx.APIErrorNotFound()
return
}
is, err := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, userToCheck.ID) is, err := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, userToCheck.ID)
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
+26
View File
@@ -264,6 +264,32 @@ func testAPIOrgGeneral(t *testing.T) {
}) })
} }
func TestAPIOrgPrivateMembersNotLeaked(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// privated_org (org 23) has private visibility and a single member, user5
const orgName = "privated_org"
const memberName = "user5"
// member publicizes their own membership inside the private org
memberSession := loginUser(t, memberName)
memberToken := getTokenForLoggedInUser(t, memberSession, auth_model.AccessTokenScopeWriteOrganization)
req := NewRequest(t, "PUT", "/api/v1/orgs/"+orgName+"/public_members/"+memberName).AddTokenAuth(memberToken)
MakeRequest(t, req, http.StatusNoContent)
// an outsider must not be able to learn about the membership of a private org
outsiderSession := loginUser(t, "user2")
outsiderToken := getTokenForLoggedInUser(t, outsiderSession, auth_model.AccessTokenScopeReadOrganization)
req = NewRequest(t, "GET", "/api/v1/orgs/"+orgName+"/public_members/"+memberName).AddTokenAuth(outsiderToken)
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "GET", "/api/v1/orgs/"+orgName+"/public_members").AddTokenAuth(outsiderToken)
MakeRequest(t, req, http.StatusNotFound)
// the member can still see the public membership of their own org
req = NewRequest(t, "GET", "/api/v1/orgs/"+orgName+"/public_members/"+memberName).AddTokenAuth(memberToken)
MakeRequest(t, req, http.StatusNoContent)
}
func testAPIDeleteOrgRepos(t *testing.T) { func testAPIDeleteOrgRepos(t *testing.T) {
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"}) org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
orgRepos, err := repo_model.GetOrgRepositories(t.Context(), org3.ID) orgRepos, err := repo_model.GetOrgRepositories(t.Context(), org3.ID)