Skip to content

Commit eecf23d

Browse files
CopilotfrjcompCopilot
authored
Refactor GitLab access levels to log human-readable names (#567)
* Refactor GitLab access levels to log human-readable names instead of numbers --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: frjcomp <107982661+frjcomp@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 6cd145a commit eecf23d

4 files changed

Lines changed: 71 additions & 9 deletions

File tree

pkg/gitlab/enum/enum.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/CompassSecurity/pipeleek/pkg/gitlab/util"
1111
"github.com/rs/zerolog/log"
12+
gitlab "gitlab.com/gitlab-org/api/client-go"
1213

1314
"resty.dev/v3"
1415
)
@@ -157,12 +158,12 @@ func listTokenAssociations(client resty.Client, baseUrl string, pat string, acce
157158
}
158159

159160
for _, group := range resp.Groups {
160-
log.Warn().Str("group", group.WebURL).Int("accessLevel", group.AccessLevels).Str("name", group.Name).Str("visibility", string(group.Visibility)).Msg("Group")
161+
log.Warn().Str("group", group.WebURL).Str("accessLevel", util.AccessLevelName(gitlab.AccessLevelValue(group.AccessLevels))).Str("name", group.Name).Str("visibility", string(group.Visibility)).Msg("Group")
161162
log.Debug().Interface("full_group", group).Msg("Full Group details")
162163
}
163164

164165
for _, project := range resp.Projects {
165-
log.Warn().Str("project", project.WebURL).Str("name", project.NameWithNamespace).Int("groupAccessLevel", project.AccessLevels.GroupAccessLevel).Int("projectAccessLevel", project.AccessLevels.ProjectAccessLevel).Msg("Project")
166+
log.Warn().Str("project", project.WebURL).Str("name", project.NameWithNamespace).Str("groupAccessLevel", util.AccessLevelName(gitlab.AccessLevelValue(project.AccessLevels.GroupAccessLevel))).Str("projectAccessLevel", util.AccessLevelName(gitlab.AccessLevelValue(project.AccessLevels.ProjectAccessLevel))).Msg("Project")
166167
log.Debug().Interface("full_project", project).Msg("Full Project details")
167168
}
168169

pkg/gitlab/renovate/privesc/privesc.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func RunExploit(gitlabUrl, gitlabApiToken, repoName, renovateBranchesRegex, moni
3737

3838
projectAccessLevel := getUserAccessLevel(project)
3939
if projectAccessLevel < gogitlab.DeveloperPermissions {
40-
log.Fatal().Any("projectAccessLevel", projectAccessLevel).Msg("You (probably) need at least Developer permissions to exploit this vulnerability, you must be able to push to the Renovate Bot created branches branches")
40+
log.Fatal().Str("projectAccessLevel", util.AccessLevelName(projectAccessLevel)).Msg("You (probably) need at least Developer permissions to exploit this vulnerability, you must be able to push to the Renovate Bot created branches")
4141
}
4242

4343
ciCdYml, err := util.FetchCICDYml(git, project.ID)
@@ -91,20 +91,20 @@ func checkDefaultBranchProtections(git *gogitlab.Client, project *gogitlab.Proje
9191
}
9292

9393
for _, accessLevel := range protectedbranch.PushAccessLevels {
94-
log.Debug().Str("branch", project.DefaultBranch).Any("userAccessLevel", currentAccessLevel).Any("requiredAccessLevel", accessLevel.AccessLevel).Msg("Testing push access level for default branch")
94+
log.Debug().Str("branch", project.DefaultBranch).Str("userAccessLevel", util.AccessLevelName(currentAccessLevel)).Str("requiredAccessLevel", util.AccessLevelName(accessLevel.AccessLevel)).Msg("Testing push access level for default branch")
9595
if currentAccessLevel >= accessLevel.AccessLevel {
96-
log.Fatal().Str("branch", project.DefaultBranch).Any("userAccessLevel", currentAccessLevel).Any("requiredAccessLevel", accessLevel.AccessLevel).Msg("You can already push to the default branch, no need to exploit")
96+
log.Fatal().Str("branch", project.DefaultBranch).Str("userAccessLevel", util.AccessLevelName(currentAccessLevel)).Str("requiredAccessLevel", util.AccessLevelName(accessLevel.AccessLevel)).Msg("You can already push to the default branch, no need to exploit")
9797
}
9898
}
9999

100100
for _, accessLevel := range protectedbranch.MergeAccessLevels {
101-
log.Debug().Str("branch", project.DefaultBranch).Any("userAccessLevel", currentAccessLevel).Any("requiredAccessLevel", accessLevel.AccessLevel).Msg("Testing merge access level for default branch")
101+
log.Debug().Str("branch", project.DefaultBranch).Str("userAccessLevel", util.AccessLevelName(currentAccessLevel)).Str("requiredAccessLevel", util.AccessLevelName(accessLevel.AccessLevel)).Msg("Testing merge access level for default branch")
102102
if currentAccessLevel >= accessLevel.AccessLevel {
103-
log.Fatal().Str("branch", project.DefaultBranch).Any("userAccessLevel", currentAccessLevel).Any("requiredAccessLevel", accessLevel.AccessLevel).Msg("You can already merge to the default branch, no need to exploit")
103+
log.Fatal().Str("branch", project.DefaultBranch).Str("userAccessLevel", util.AccessLevelName(currentAccessLevel)).Str("requiredAccessLevel", util.AccessLevelName(accessLevel.AccessLevel)).Msg("You can already merge to the default branch, no need to exploit")
104104
}
105105
}
106106

107-
log.Info().Str("branch", project.DefaultBranch).Any("currentAccessLevel", currentAccessLevel).Msg("Default branch is protected and you do not have direct access, proceeding with exploit")
107+
log.Info().Str("branch", project.DefaultBranch).Str("currentAccessLevel", util.AccessLevelName(currentAccessLevel)).Msg("Default branch is protected and you do not have direct access, proceeding with exploit")
108108
}
109109

110110
func monitorBranches(git *gogitlab.Client, project *gogitlab.Project, branchMonitor *pkgrenovate.BranchMonitor, monitoringInterval time.Duration) *gogitlab.Branch {

pkg/gitlab/util/util.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package util
22

33
import (
44
"errors"
5+
"fmt"
56
"io"
67
"net/http"
78
"net/url"
@@ -17,6 +18,32 @@ import (
1718
gitlab "gitlab.com/gitlab-org/api/client-go"
1819
)
1920

21+
// AccessLevelName returns the human-readable name for a GitLab access level value.
22+
func AccessLevelName(level gitlab.AccessLevelValue) string {
23+
switch level {
24+
case gitlab.NoPermissions:
25+
return "No access"
26+
case gitlab.MinimalAccessPermissions:
27+
return "Minimal access"
28+
case gitlab.GuestPermissions:
29+
return "Guest"
30+
case gitlab.PlannerPermissions:
31+
return "Planner"
32+
case gitlab.ReporterPermissions:
33+
return "Reporter"
34+
case gitlab.DeveloperPermissions:
35+
return "Developer"
36+
case gitlab.MaintainerPermissions:
37+
return "Maintainer"
38+
case gitlab.OwnerPermissions:
39+
return "Owner"
40+
case gitlab.AdminPermissions:
41+
return "Admin"
42+
default:
43+
return fmt.Sprintf("Unknown (%d)", int(level))
44+
}
45+
}
46+
2047
// ProjectIteratorFunc is a callback function type for processing each project
2148
type ProjectIteratorFunc func(project *gitlab.Project) error
2249

pkg/gitlab/util/util_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,41 @@ import (
1212
gitlab "gitlab.com/gitlab-org/api/client-go"
1313
)
1414

15-
// TestDetermineVersion_ParsesVersion ensures the help page parsing extracts instance_version.
15+
// TestAccessLevelName_KnownLevels ensures all known access levels return correct names.
16+
func TestAccessLevelName_KnownLevels(t *testing.T) {
17+
tests := []struct {
18+
level gitlab.AccessLevelValue
19+
expected string
20+
}{
21+
{gitlab.NoPermissions, "No access"},
22+
{gitlab.MinimalAccessPermissions, "Minimal access"},
23+
{gitlab.GuestPermissions, "Guest"},
24+
{gitlab.PlannerPermissions, "Planner"},
25+
{gitlab.ReporterPermissions, "Reporter"},
26+
{gitlab.DeveloperPermissions, "Developer"},
27+
{gitlab.MaintainerPermissions, "Maintainer"},
28+
{gitlab.OwnerPermissions, "Owner"},
29+
{gitlab.AdminPermissions, "Admin"},
30+
}
31+
32+
for _, tt := range tests {
33+
t.Run(tt.expected, func(t *testing.T) {
34+
result := AccessLevelName(tt.level)
35+
if result != tt.expected {
36+
t.Fatalf("AccessLevelName(%d) = %q, want %q", int(tt.level), result, tt.expected)
37+
}
38+
})
39+
}
40+
}
41+
42+
// TestAccessLevelName_UnknownLevel ensures unknown access levels return a descriptive fallback.
43+
func TestAccessLevelName_UnknownLevel(t *testing.T) {
44+
result := AccessLevelName(gitlab.AccessLevelValue(99))
45+
expected := "Unknown (99)"
46+
if result != expected {
47+
t.Fatalf("AccessLevelName(99) = %q, want %q", result, expected)
48+
}
49+
}
1650
func TestDetermineVersion_ParsesVersion(t *testing.T) {
1751
// Simulate GitLab /help endpoint content containing instance_version JSON fragment
1852
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)