Skip to content

Commit c8aaf4f

Browse files
committed
refactor(#9): move gitlab logic to specific repository package
A first step to adding github is to isolate gitlab
1 parent 10df2a4 commit c8aaf4f

File tree

16 files changed

+183
-122
lines changed

16 files changed

+183
-122
lines changed

internal/cli/patrol.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import (
66
"os/exec"
77
"sheriff/internal/config"
88
"sheriff/internal/git"
9-
"sheriff/internal/gitlab"
109
"sheriff/internal/patrol"
10+
"sheriff/internal/repo"
1111
"sheriff/internal/scanner"
1212
"sheriff/internal/slack"
1313
"strings"
1414

15+
"github.com/rs/zerolog/log"
1516
"github.com/urfave/cli/v2"
1617
)
1718

@@ -124,7 +125,7 @@ func PatrolAction(cCtx *cli.Context) error {
124125
slackToken := cCtx.String(slackTokenFlag)
125126

126127
// Create services
127-
gitlabService, err := gitlab.New(gitlabToken)
128+
gitlabService, err := repo.NewGitlabService(gitlabToken)
128129
if err != nil {
129130
return errors.Join(errors.New("failed to create GitLab service"), err)
130131
}
@@ -149,7 +150,8 @@ func PatrolAction(cCtx *cli.Context) error {
149150
if warn, err := patrolService.Patrol(config); err != nil {
150151
return errors.Join(errors.New("failed to scan"), err)
151152
} else if warn != nil {
152-
return cli.Exit("Patrol was partially successful, some errors occurred. Check the logs for more information.", 1)
153+
log.Err(warn).Msg("Patrol was partially successful, some errors occurred.")
154+
return cli.Exit("Exiting patrol with partial success", 1)
153155
}
154156

155157
return nil

internal/patrol/patrol.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ import (
88
"os"
99
"sheriff/internal/config"
1010
"sheriff/internal/git"
11-
"sheriff/internal/gitlab"
1211
"sheriff/internal/publish"
12+
"sheriff/internal/repo"
1313
"sheriff/internal/scanner"
1414
"sheriff/internal/slack"
1515
"sync"
1616

1717
"github.com/elliotchance/pie/v2"
1818
"github.com/rs/zerolog/log"
19-
gogitlab "github.com/xanzy/go-gitlab"
2019
"golang.org/x/exp/slices"
2120
)
2221

@@ -30,7 +29,7 @@ type securityPatroller interface {
3029

3130
// sheriffService is the implementation of the SecurityPatroller interface.
3231
type sheriffService struct {
33-
gitlabService gitlab.IService
32+
gitlabService repo.IService
3433
slackService slack.IService
3534
gitService git.IService
3635
osvService scanner.VulnScanner[scanner.OsvReport]
@@ -39,7 +38,7 @@ type sheriffService struct {
3938
// New creates a new securityPatroller service.
4039
// It contains the main "loop" logic of this tool.
4140
// A "patrol" is defined as scanning GitLab groups for vulnerabilities and publishing reports where needed.
42-
func New(gitlabService gitlab.IService, slackService slack.IService, gitService git.IService, osvService scanner.VulnScanner[scanner.OsvReport]) securityPatroller {
41+
func New(gitlabService repo.IService, slackService slack.IService, gitService git.IService, osvService scanner.VulnScanner[scanner.OsvReport]) securityPatroller {
4342
return &sheriffService{
4443
gitlabService: gitlabService,
4544
slackService: slackService,
@@ -127,10 +126,10 @@ func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation) (
127126
wg.Add(1)
128127
go func(reportsChan chan<- scanner.Report) {
129128
defer wg.Done()
130-
log.Info().Str("project", project.Name).Msg("Scanning project")
129+
log.Info().Str("project", project.Path).Msg("Scanning project")
131130
if report, err := s.scanProject(project); err != nil {
132-
log.Error().Err(err).Str("project", project.Name).Msg("Failed to scan project, skipping.")
133-
err = errors.Join(fmt.Errorf("failed to scan project %v", project.Name), err)
131+
log.Error().Err(err).Str("project", project.Path).Msg("Failed to scan project, skipping.")
132+
err = errors.Join(fmt.Errorf("failed to scan project %v", project.Path), err)
134133
warn = errors.Join(err, warn)
135134
reportsChan <- scanner.Report{Project: project, Error: true}
136135
} else {
@@ -154,31 +153,31 @@ func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation) (
154153
}
155154

156155
// scanProject scans a project for vulnerabilities using the osv scanner.
157-
func (s *sheriffService) scanProject(project gogitlab.Project) (report *scanner.Report, err error) {
156+
func (s *sheriffService) scanProject(project repo.Project) (report *scanner.Report, err error) {
158157
dir, err := os.MkdirTemp(tempScanDir, fmt.Sprintf("%v-", project.Name))
159158
if err != nil {
160159
return nil, errors.Join(errors.New("failed to create project temporary directory"), err)
161160
}
162161
defer os.RemoveAll(dir)
163162

164163
// Clone the project
165-
log.Info().Str("project", project.Name).Str("dir", dir).Msg("Cloning project")
166-
if err = s.gitService.Clone(dir, project.HTTPURLToRepo); err != nil {
164+
log.Info().Str("project", project.Path).Str("dir", dir).Msg("Cloning project")
165+
if err = s.gitService.Clone(dir, project.RepoUrl); err != nil {
167166
return nil, errors.Join(errors.New("failed to clone project"), err)
168167
}
169168

170-
config := config.GetProjectConfiguration(project.NameWithNamespace, dir)
169+
config := config.GetProjectConfiguration(project.Path, dir)
171170

172171
// Scan the project
173-
log.Info().Str("project", project.Name).Msg("Running osv-scanner")
172+
log.Info().Str("project", project.Path).Msg("Running osv-scanner")
174173
osvReport, err := s.osvService.Scan(dir)
175174
if err != nil {
176-
log.Error().Err(err).Str("project", project.Name).Msg("Failed to run osv-scanner")
175+
log.Error().Err(err).Str("project", project.Path).Msg("Failed to run osv-scanner")
177176
return nil, errors.Join(errors.New("failed to run osv-scanner"), err)
178177
}
179178

180179
r := s.osvService.GenerateReport(project, osvReport)
181-
log.Info().Str("project", project.Name).Msg("Finished scanning with osv-scanner")
180+
log.Info().Str("project", project.Path).Msg("Finished scanning with osv-scanner")
182181

183182
r.ProjectConfig = config
184183

internal/patrol/patrol_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package patrol
22

33
import (
44
"sheriff/internal/config"
5+
"sheriff/internal/repo"
56
"sheriff/internal/scanner"
67
"testing"
78

89
"github.com/slack-go/slack"
910
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/mock"
11-
"github.com/xanzy/go-gitlab"
1212
)
1313

1414
func TestNewService(t *testing.T) {
@@ -19,7 +19,7 @@ func TestNewService(t *testing.T) {
1919

2020
func TestScanNoProjects(t *testing.T) {
2121
mockGitlabService := &mockGitlabService{}
22-
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]gitlab.Project{}, nil)
22+
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]repo.Project{}, nil)
2323

2424
mockSlackService := &mockSlackService{}
2525

@@ -49,7 +49,7 @@ func TestScanNoProjects(t *testing.T) {
4949

5050
func TestScanNonVulnerableProject(t *testing.T) {
5151
mockGitlabService := &mockGitlabService{}
52-
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]gitlab.Project{{Name: "Hello World", HTTPURLToRepo: "https://gitlab.com/group/to/scan.git"}}, nil)
52+
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]repo.Project{{Name: "Hello World", RepoUrl: "https://gitlab.com/group/to/scan.git"}}, nil)
5353
mockGitlabService.On("CloseVulnerabilityIssue", mock.Anything).Return(nil)
5454

5555
mockSlackService := &mockSlackService{}
@@ -82,8 +82,8 @@ func TestScanNonVulnerableProject(t *testing.T) {
8282

8383
func TestScanVulnerableProject(t *testing.T) {
8484
mockGitlabService := &mockGitlabService{}
85-
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]gitlab.Project{{Name: "Hello World", HTTPURLToRepo: "https://gitlab.com/group/to/scan.git"}}, nil)
86-
mockGitlabService.On("OpenVulnerabilityIssue", mock.Anything, mock.Anything).Return(&gitlab.Issue{}, nil)
85+
mockGitlabService.On("GetProjectList", []string{"group/to/scan"}).Return([]repo.Project{{Name: "Hello World", RepoUrl: "https://gitlab.com/group/to/scan.git"}}, nil)
86+
mockGitlabService.On("OpenVulnerabilityIssue", mock.Anything, mock.Anything).Return(&repo.Issue{}, nil)
8787

8888
mockSlackService := &mockSlackService{}
8989
mockSlackService.On("PostMessage", "channel", mock.Anything).Return("", nil)
@@ -190,19 +190,19 @@ type mockGitlabService struct {
190190
mock.Mock
191191
}
192192

193-
func (c *mockGitlabService) GetProjectList(paths []string) ([]gitlab.Project, error) {
193+
func (c *mockGitlabService) GetProjectList(paths []string) ([]repo.Project, error) {
194194
args := c.Called(paths)
195-
return args.Get(0).([]gitlab.Project), args.Error(1)
195+
return args.Get(0).([]repo.Project), args.Error(1)
196196
}
197197

198-
func (c *mockGitlabService) CloseVulnerabilityIssue(project gitlab.Project) error {
198+
func (c *mockGitlabService) CloseVulnerabilityIssue(project repo.Project) error {
199199
args := c.Called(project)
200200
return args.Error(0)
201201
}
202202

203-
func (c *mockGitlabService) OpenVulnerabilityIssue(project gitlab.Project, report string) (*gitlab.Issue, error) {
203+
func (c *mockGitlabService) OpenVulnerabilityIssue(project repo.Project, report string) (*repo.Issue, error) {
204204
args := c.Called(project, report)
205-
return args.Get(0).(*gitlab.Issue), args.Error(1)
205+
return args.Get(0).(*repo.Issue), args.Error(1)
206206
}
207207

208208
type mockSlackService struct {
@@ -232,7 +232,7 @@ func (c *mockOSVService) Scan(dir string) (*scanner.OsvReport, error) {
232232
return args.Get(0).(*scanner.OsvReport), args.Error(1)
233233
}
234234

235-
func (c *mockOSVService) GenerateReport(p gitlab.Project, r *scanner.OsvReport) scanner.Report {
235+
func (c *mockOSVService) GenerateReport(p repo.Project, r *scanner.OsvReport) scanner.Report {
236236
args := c.Called(p, r)
237237
return args.Get(0).(scanner.Report)
238238
}

internal/publish/to_console.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func formatReportsMessageForConsole(scanReports []scanner.Report) string {
3232
r.WriteString(fmt.Sprintf("Total number of projects scanned: %v\n", len(scanReports)))
3333
for _, report := range scanReports {
3434
r.WriteString(fmt.Sprintln("---------------------------------"))
35-
r.WriteString(fmt.Sprintf("%v\n", report.Project.PathWithNamespace))
35+
r.WriteString(fmt.Sprintf("%v\n", report.Project.Path))
3636
r.WriteString(fmt.Sprintf("\tProject URL: %v\n", report.Project.WebURL))
3737
r.WriteString(fmt.Sprintf("\tNumber of vulnerabilities: %v\n", len(report.Vulnerabilities)))
3838
}

internal/publish/to_console_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package publish
22

33
import (
4+
"sheriff/internal/repo"
45
"sheriff/internal/scanner"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
8-
gogitlab "github.com/xanzy/go-gitlab"
99
)
1010

1111
func TestFormatReportMessageForConsole(t *testing.T) {
1212
reports := []scanner.Report{
1313
{
14-
Project: gogitlab.Project{
14+
Project: repo.Project{
1515
Name: "project1",
1616
WebURL: "http://example.com",
1717
},
@@ -27,7 +27,7 @@ func TestFormatReportMessageForConsole(t *testing.T) {
2727
},
2828
},
2929
{
30-
Project: gogitlab.Project{
30+
Project: repo.Project{
3131
Name: "project2",
3232
WebURL: "http://example2.com",
3333
},

internal/publish/to_gitlab.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package publish
33
import (
44
"errors"
55
"fmt"
6-
"sheriff/internal/gitlab"
6+
"sheriff/internal/repo"
77
"sheriff/internal/scanner"
88
"strconv"
99
"sync"
@@ -22,24 +22,24 @@ var now = time.Now
2222

2323
// PublishAsGitlabIssues creates or updates GitLab Issue reports for the given reports
2424
// It will add the Issue URL to the Report if it was created or updated successfully
25-
func PublishAsGitlabIssues(reports []scanner.Report, s gitlab.IService) (warn error) {
25+
func PublishAsGitlabIssues(reports []scanner.Report, s repo.IService) (warn error) {
2626
var wg sync.WaitGroup
2727
for i := 0; i < len(reports); i++ {
2828
wg.Add(1)
2929
go func() {
3030
defer wg.Done()
3131
if reports[i].IsVulnerable {
3232
if issue, err := s.OpenVulnerabilityIssue(reports[i].Project, formatGitlabIssue(reports[i])); err != nil {
33-
log.Error().Err(err).Str("project", reports[i].Project.Name).Msg("Failed to open or update issue")
34-
err = fmt.Errorf("failed to open or update issue for project %v", reports[i].Project.Name)
33+
log.Error().Err(err).Str("project", reports[i].Project.Path).Msg("Failed to open or update issue")
34+
err = fmt.Errorf("failed to open or update issue for project %v", reports[i].Project.Path)
3535
warn = errors.Join(err, warn)
3636
} else {
3737
reports[i].IssueUrl = issue.WebURL
3838
}
3939
} else {
4040
if err := s.CloseVulnerabilityIssue(reports[i].Project); err != nil {
41-
log.Error().Err(err).Str("project", reports[i].Project.Name).Msg("Failed to close issue")
42-
err = fmt.Errorf("failed to close issue for project %v", reports[i].Project.Name)
41+
log.Error().Err(err).Str("project", reports[i].Project.Path).Msg("Failed to close issue")
42+
err = fmt.Errorf("failed to close issue for project %v", reports[i].Project.Path)
4343
warn = errors.Join(err, warn)
4444
}
4545
}

internal/publish/to_gitlab_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package publish
22

33
import (
4+
"sheriff/internal/repo"
45
"sheriff/internal/scanner"
56
"testing"
67
"time"
78

89
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/mock"
10-
"github.com/xanzy/go-gitlab"
1111
)
1212

1313
// Severities are grouped by severity score kind
@@ -164,7 +164,7 @@ func TestMarkdownBoolean(t *testing.T) {
164164

165165
func TestPublishAsGitlabIssues(t *testing.T) {
166166
mockGitlabService := &mockGitlabService{}
167-
mockGitlabService.On("OpenVulnerabilityIssue", mock.Anything, mock.Anything).Return(&gitlab.Issue{WebURL: "https://my-issue.com"}, nil)
167+
mockGitlabService.On("OpenVulnerabilityIssue", mock.Anything, mock.Anything).Return(&repo.Issue{WebURL: "https://my-issue.com"}, nil)
168168
reports := []scanner.Report{
169169
{
170170
IsVulnerable: true,
@@ -206,17 +206,17 @@ type mockGitlabService struct {
206206
mock.Mock
207207
}
208208

209-
func (c *mockGitlabService) GetProjectList(paths []string) ([]gitlab.Project, error) {
209+
func (c *mockGitlabService) GetProjectList(paths []string) ([]repo.Project, error) {
210210
args := c.Called(paths)
211-
return args.Get(0).([]gitlab.Project), args.Error(1)
211+
return args.Get(0).([]repo.Project), args.Error(1)
212212
}
213213

214-
func (c *mockGitlabService) CloseVulnerabilityIssue(project gitlab.Project) error {
214+
func (c *mockGitlabService) CloseVulnerabilityIssue(project repo.Project) error {
215215
args := c.Called(project)
216216
return args.Error(0)
217217
}
218218

219-
func (c *mockGitlabService) OpenVulnerabilityIssue(project gitlab.Project, report string) (*gitlab.Issue, error) {
219+
func (c *mockGitlabService) OpenVulnerabilityIssue(project repo.Project, report string) (*repo.Issue, error) {
220220
args := c.Called(project, report)
221-
return args.Get(0).(*gitlab.Issue), args.Error(1)
221+
return args.Get(0).(*repo.Issue), args.Error(1)
222222
}

internal/publish/to_slack.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func formatSpecificChannelSlackMessage(report scanner.Report) []goslack.MsgOptio
8282

8383
// Texts
8484
title := fmt.Sprintf("Sheriff Report %v", time.Now().Format("2006-01-02"))
85-
subtitle := fmt.Sprintf("Project: <%s|*%s*>", report.Project.WebURL, report.Project.PathWithNamespace)
85+
subtitle := fmt.Sprintf("Project: <%s|*%s*>", report.Project.WebURL, report.Project.Path)
8686
var subtitleFullReport string
8787
if report.IssueUrl != "" {
8888
subtitleFullReport = fmt.Sprintf("Full report: <%s|*Full report*>", report.IssueUrl)

internal/publish/to_slack_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package publish
22

33
import (
44
"sheriff/internal/config"
5+
"sheriff/internal/repo"
56
"sheriff/internal/scanner"
67
"testing"
78

89
"github.com/slack-go/slack"
910
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/mock"
11-
gogitlab "github.com/xanzy/go-gitlab"
1212
)
1313

1414
func TestPublishAsGeneralSlackMessage(t *testing.T) {
@@ -102,7 +102,7 @@ func TestFormatReportMessage(t *testing.T) {
102102
reportBySeverityKind := map[scanner.SeverityScoreKind][]scanner.Report{
103103
scanner.Critical: {
104104
{
105-
Project: gogitlab.Project{
105+
Project: repo.Project{
106106
Name: "project1",
107107
WebURL: "http://example.com",
108108
},
@@ -118,7 +118,7 @@ func TestFormatReportMessage(t *testing.T) {
118118
},
119119
scanner.High: {
120120
{
121-
Project: gogitlab.Project{
121+
Project: repo.Project{
122122
Name: "project2",
123123
WebURL: "http://example2.com",
124124
},

0 commit comments

Comments
 (0)