Skip to content

Commit 5d4e011

Browse files
Copilotfrjcomp
andauthored
Add Gitea enum command (#304)
* Implemented Gitea Enum Command --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: frjcomp <107982661+frjcomp@users.noreply.github.com>
1 parent b7052f0 commit 5d4e011

20 files changed

Lines changed: 278 additions & 48 deletions

File tree

.github/workflows/golangci-lint.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ jobs:
1313
name: lint
1414
runs-on: ubuntu-latest
1515
steps:
16-
- uses: actions/checkout@v4
17-
- uses: actions/setup-go@v5
16+
- uses: actions/checkout@v5
17+
- uses: actions/setup-go@v6
1818
with:
1919
go-version: stable
2020
- name: golangci-lint
21-
uses: golangci/golangci-lint-action@v6
21+
uses: golangci/golangci-lint-action@v8
2222
with:
2323
version: latest
2424
working-directory: ./src/pipeleak

src/pipeleak/cmd/bitbucket/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewClient(username string, password string, bitBucketCookie string) BitBuck
3838
bbClient := BitBucketApiClient{Client: client}
3939
bbClient.Client.AddRetryHooks(
4040
func(res *resty.Response, err error) {
41-
if 429 == res.StatusCode() {
41+
if res.StatusCode() == 429 {
4242
log.Info().Int("status", res.StatusCode()).Msg("Retrying request, we are rate limited")
4343
} else {
4444
log.Info().Int("status", res.StatusCode()).Msg("Retrying request, not due to rate limit")

src/pipeleak/cmd/devops/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func NewClient(username string, password string) AzureDevOpsApiClient {
1919
bbClient := AzureDevOpsApiClient{Client: *resty.New().SetBasicAuth(username, password).SetRedirectPolicy(resty.FlexibleRedirectPolicy(5))}
2020
bbClient.Client.AddRetryHooks(
2121
func(res *resty.Response, err error) {
22-
if 429 == res.StatusCode() {
22+
if res.StatusCode() == 429 {
2323
log.Info().Int("status", res.StatusCode()).Msg("Retrying request, we are rate limited")
2424
} else {
2525
log.Info().Int("status", res.StatusCode()).Msg("Retrying request, not due to rate limit")

src/pipeleak/cmd/devops/scan.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ func analyzeArtifact(client AzureDevOpsApiClient, artifact Artifact, buildWebUrl
277277
} else if filetype.IsArchive(content) {
278278
scanner.HandleArchiveArtifact(file.Name, content, buildWebUrl, artifact.Name, options.TruffleHogVerification)
279279
}
280-
fc.Close()
280+
_ = fc.Close()
281281
})
282282
}
283283

src/pipeleak/cmd/docs/docs.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func generateDocs(cmd *cobra.Command, dir string, level int) error {
5959
if err != nil {
6060
return err
6161
}
62-
defer f.Close()
62+
defer func() { _ = f.Close() }()
6363

6464
customLinkHandler := func(s string) string {
6565
if s == "pipeleak.md" {
@@ -354,12 +354,12 @@ func copyFile(src, dst string) error {
354354
if err != nil {
355355
return err
356356
}
357-
defer in.Close()
357+
defer func() { _ = in.Close() }()
358358
out, err := os.Create(dst)
359359
if err != nil {
360360
return err
361361
}
362-
defer out.Close()
362+
defer func() { _ = out.Close() }()
363363
_, err = io.Copy(out, in)
364364
return err
365365
}

src/pipeleak/cmd/gitea/enum.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package gitea
2+
3+
import (
4+
"code.gitea.io/sdk/gitea"
5+
"github.com/CompassSecurity/pipeleak/helper"
6+
"github.com/rs/zerolog/log"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func NewEnumCmd() *cobra.Command {
11+
enumCmd := &cobra.Command{
12+
Use: "enum",
13+
Short: "Enumerate access of a Gitea token",
14+
Long: "Enumerate access rights of a Gitea access token by retrieving the authenticated user's information, organizations with access levels, and all accessible repositories with permissions.",
15+
Example: `pipeleak gitea enum --token [tokenval] --gitea https://gitea.mycompany.com`,
16+
Run: Enum,
17+
}
18+
enumCmd.Flags().StringVarP(&giteaUrl, "gitea", "g", "https://gitea.com", "Gitea instance URL")
19+
enumCmd.Flags().StringVarP(&giteaApiToken, "token", "t", "", "Gitea API Token")
20+
21+
return enumCmd
22+
}
23+
24+
func Enum(cmd *cobra.Command, args []string) {
25+
helper.SetLogLevel(verbose)
26+
27+
client, err := gitea.NewClient(giteaUrl, gitea.SetToken(giteaApiToken))
28+
if err != nil {
29+
log.Fatal().Stack().Err(err).Msg("Failed creating gitea client")
30+
return
31+
}
32+
33+
log.Info().Msg("Enumerating User")
34+
user, _, err := client.GetMyUserInfo()
35+
if err != nil {
36+
log.Fatal().Stack().Err(err).Msg("Failed fetching current user")
37+
return
38+
}
39+
40+
log.Debug().Interface("user", user).Msg("Full user data structure")
41+
42+
log.Warn().
43+
Int64("id", user.ID).
44+
Str("username", user.UserName).
45+
Str("fullName", user.FullName).
46+
Str("email", user.Email).
47+
Str("description", user.Description).
48+
Bool("isAdmin", user.IsAdmin).
49+
Bool("isActive", user.IsActive).
50+
Bool("restricted", user.Restricted).
51+
Msg("Current user")
52+
53+
log.Info().Msg("Enumerating Organizations")
54+
55+
orgPage := 1
56+
for {
57+
orgs, resp, err := client.ListMyOrgs(gitea.ListOrgsOptions{
58+
ListOptions: gitea.ListOptions{
59+
Page: orgPage,
60+
PageSize: 50,
61+
},
62+
})
63+
64+
if err != nil {
65+
log.Error().Stack().Err(err).Msg("Failed fetching organizations")
66+
break
67+
}
68+
69+
for _, org := range orgs {
70+
orgPerms, _, err := client.GetOrgPermissions(org.UserName, user.UserName)
71+
72+
if err != nil {
73+
log.Debug().Str("org", org.UserName).Err(err).Msg("Failed to get org permissions")
74+
}
75+
76+
logEvent := log.Warn().
77+
Int64("id", org.ID).
78+
Str("name", org.UserName).
79+
Str("fullName", org.FullName).
80+
Str("website", org.Website).
81+
Str("description", org.Description).
82+
Str("visibility", org.Visibility)
83+
84+
if orgPerms != nil {
85+
logEvent = logEvent.
86+
Bool("isOwner", orgPerms.IsOwner).
87+
Bool("isAdmin", orgPerms.IsAdmin).
88+
Bool("canWrite", orgPerms.CanWrite).
89+
Bool("canRead", orgPerms.CanRead).
90+
Bool("canCreateRepo", orgPerms.CanCreateRepository)
91+
}
92+
93+
logEvent.Msg("Organization")
94+
95+
repoPage := 1
96+
for {
97+
orgRepos, repoResp, err := client.ListOrgRepos(org.UserName, gitea.ListOrgReposOptions{
98+
ListOptions: gitea.ListOptions{
99+
Page: repoPage,
100+
PageSize: 50,
101+
},
102+
})
103+
104+
if err != nil {
105+
log.Debug().Str("org", org.UserName).Err(err).Msg("Failed to list org repositories")
106+
break
107+
}
108+
109+
for _, repo := range orgRepos {
110+
logRepo := log.Warn().
111+
Int64("id", repo.ID).
112+
Str("name", repo.Name).
113+
Str("fullName", repo.FullName).
114+
Str("owner", repo.Owner.UserName).
115+
Str("description", repo.Description).
116+
Bool("private", repo.Private).
117+
Bool("archived", repo.Archived).
118+
Str("url", repo.HTMLURL)
119+
120+
if repo.Permissions != nil {
121+
logRepo = logRepo.
122+
Bool("admin", repo.Permissions.Admin).
123+
Bool("push", repo.Permissions.Push).
124+
Bool("pull", repo.Permissions.Pull)
125+
}
126+
127+
logRepo.Msg("Organization Repository")
128+
}
129+
130+
if repoResp == nil || repoResp.NextPage == 0 {
131+
break
132+
}
133+
134+
repoPage = repoResp.NextPage
135+
}
136+
}
137+
138+
if resp == nil || resp.NextPage == 0 {
139+
break
140+
}
141+
142+
orgPage = resp.NextPage
143+
}
144+
145+
log.Info().Msg("Enumerating User Repositories")
146+
147+
repoPage := 1
148+
for {
149+
repos, resp, err := client.ListMyRepos(gitea.ListReposOptions{
150+
ListOptions: gitea.ListOptions{
151+
Page: repoPage,
152+
PageSize: 50,
153+
},
154+
})
155+
if err != nil {
156+
log.Error().Stack().Err(err).Msg("Failed fetching user repositories")
157+
break
158+
}
159+
160+
for _, repo := range repos {
161+
logRepo := log.Warn().
162+
Int64("id", repo.ID).
163+
Str("name", repo.Name).
164+
Str("fullName", repo.FullName).
165+
Str("owner", repo.Owner.UserName).
166+
Str("description", repo.Description).
167+
Bool("private", repo.Private).
168+
Bool("archived", repo.Archived).
169+
Str("url", repo.HTMLURL)
170+
171+
if repo.Permissions != nil {
172+
logRepo = logRepo.
173+
Bool("admin", repo.Permissions.Admin).
174+
Bool("push", repo.Permissions.Push).
175+
Bool("pull", repo.Permissions.Pull)
176+
}
177+
178+
logRepo.Msg("User Repository")
179+
}
180+
181+
if resp == nil || resp.NextPage == 0 {
182+
break
183+
}
184+
repoPage = resp.NextPage
185+
}
186+
187+
log.Info().Msg("Done")
188+
}

src/pipeleak/cmd/gitea/gitea.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package gitea
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
var (
8+
giteaApiToken string
9+
giteaUrl string
10+
verbose bool
11+
)
12+
13+
func NewGiteaRootCmd() *cobra.Command {
14+
giteaCmd := &cobra.Command{
15+
Use: "gitea [command]",
16+
Short: "Gitea related commands",
17+
Long: "Commands to enumerate and exploit Gitea instances.",
18+
GroupID: "Gitea",
19+
}
20+
21+
giteaCmd.AddCommand(NewEnumCmd())
22+
23+
giteaCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbose logging")
24+
25+
return giteaCmd
26+
}

src/pipeleak/cmd/github/scan.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,7 @@ func scanAllPublicRepositories(client *github.Client, latestProjectId int64) {
225225
// thus we keep a temporary cache of the ids of the last 5 pages and check if we alredy scanned the repo id, or skip them.
226226
tmpIdCache := make(map[int64]struct{})
227227
pageCounter := 0
228-
for {
229-
if opt.Since < 0 {
230-
break
231-
}
232-
228+
for opt.Since >= 0 {
233229
if pageCounter > 4 {
234230
pageCounter = 0
235231
tmpIdCache = deleteHighestXKeys(tmpIdCache, 100)
@@ -352,10 +348,11 @@ func downloadWorkflowRunLog(client *github.Client, repo *github.Repository, work
352348
}
353349

354350
// already deleted, skip
355-
if resp.StatusCode == 410 {
351+
switch resp.StatusCode {
352+
case 410:
356353
log.Debug().Str("workflowRunName", *workflowRun.Name).Msg("Skipped expired")
357354
return
358-
} else if resp.StatusCode == 404 {
355+
case 404:
359356
return
360357
}
361358

@@ -420,7 +417,7 @@ func readZipFile(zf *zip.File) ([]byte, error) {
420417
if err != nil {
421418
return nil, err
422419
}
423-
defer f.Close()
420+
defer func() { _ = f.Close() }()
424421
return io.ReadAll(f)
425422
}
426423

@@ -545,7 +542,7 @@ func analyzeArtifact(client *github.Client, workflowRun *github.WorkflowRun, art
545542
} else if filetype.IsArchive(content) {
546543
scanner.HandleArchiveArtifact(file.Name, content, *workflowRun.HTMLURL, *workflowRun.Name, options.TruffleHogVerification)
547544
}
548-
fc.Close()
545+
_ = fc.Close()
549546
})
550547
}
551548

src/pipeleak/cmd/gitlab/nist/nist.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func FetchVulns(version string) (string, error) {
1313
if err != nil {
1414
return "{}", err
1515
}
16-
defer res.Body.Close()
16+
defer func() { _ = res.Body.Close() }()
1717

1818
if res.StatusCode == 200 {
1919
resData, err := io.ReadAll(res.Body)

src/pipeleak/cmd/gitlab/renovate/enum.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ func fetchCurrentSelfHostedOptions() []string {
379379
log.Fatal().Stack().Err(err).Msg("Failed fetching self-hosted configuration documentation")
380380
return []string{}
381381
}
382-
defer res.Body.Close()
382+
defer func() { _ = res.Body.Close() }()
383383
if res.StatusCode != 200 {
384384
log.Fatal().Int("status", res.StatusCode).Msg("Failed fetching self-hosted configuration documentation")
385385
return []string{}
@@ -434,7 +434,7 @@ func extendRenovateConfig(renovateConfig string, project *gitlab.Project) string
434434
return renovateConfig
435435
}
436436

437-
defer resp.Body.Close()
437+
defer func() { _ = resp.Body.Close() }()
438438

439439
bodyBytes, err := io.ReadAll(resp.Body)
440440
if err != nil {
@@ -469,7 +469,7 @@ func validateRenovateConfigService(serviceUrl string) error {
469469

470470
if resp.StatusCode != 200 {
471471
log.Error().Int("status", resp.StatusCode).Str("endpoint", u.String()).Msg("Renovate config service healthcheck failed")
472-
return fmt.Errorf("Renovate config service healthcheck failed: %d", resp.StatusCode)
472+
return fmt.Errorf("renovate config service healthcheck failed: %d", resp.StatusCode)
473473
}
474474

475475
return nil

0 commit comments

Comments
 (0)