From 33257ff50ee02f8b12a5016ba58324fa6140a4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Insaurralde?= Date: Thu, 16 Jul 2026 18:27:19 -0300 Subject: [PATCH] fix(controlplane): authorize GroupService/ListProjects at the service layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ListProjects was the only group sub-resource handler that resolved results without a service-layer permission check, relying solely on the visibleProjects row filter. That filter returns nil ("RBAC not applied") for a legacy RoleViewer and for org-scoped API tokens, and the data layer treats nil as "no filter", so those callers received the full group->project attachment map that the design restricts to org admins/maintainers. Add the same userHasPermissionOnGroupMembershipsWithPolicy guard the sibling handlers use (PolicyGroupListMemberships) before resolving projects, matching the ListMembers fix. Chainloop-Trace-Sessions: ebb05958-d85c-4d2e-9f65-5cd74dac91ee Signed-off-by: Matías Insaurralde --- app/controlplane/internal/service/group.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/controlplane/internal/service/group.go b/app/controlplane/internal/service/group.go index 0ba45abda..8a0aaa933 100644 --- a/app/controlplane/internal/service/group.go +++ b/app/controlplane/internal/service/group.go @@ -1,5 +1,5 @@ // -// Copyright 2025 The Chainloop Authors. +// Copyright 2025-2026 The Chainloop Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -528,6 +528,15 @@ func (g *GroupService) ListProjects(ctx context.Context, req *pb.GroupServiceLis return nil, errors.BadRequest("invalid", fmt.Sprintf("invalid group reference: %s", err.Error())) } + // Authorize before resolving results. This mirrors the sibling group sub-resource handlers + // (ListMembers, etc.): only org admins/owners or a maintainer of the target group may read a + // group's project attachments. The visibleProjects row filter alone is insufficient because it + // returns nil ("no filter") for callers where RBAC is not applied (e.g. a legacy RoleViewer or + // an org-scoped API token), which the data layer treats as full visibility. + if err = g.userHasPermissionOnGroupMembershipsWithPolicy(ctx, currentOrg.ID, req.GetGroupReference(), authz.PolicyGroupListMemberships); err != nil { + return nil, err + } + // Initialize the options for getting projects groupOpts := &biz.ListProjectsByGroupOpts{ IdentityReference: &biz.IdentityReference{