Skip to content

Commit ad21156

Browse files
authored
Merge pull request #55 from sacha-c/ignore-project-list
feat: ignore project list
2 parents c29b4e9 + 21a4576 commit ad21156

File tree

7 files changed

+67
-4
lines changed

7 files changed

+67
-4
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Sheriff is a tool to scan repositories and generate security reports.
2525
- [verbose](#verbose)
2626
- [Scanning](#scanning)
2727
- [targets](#targets)
28+
- [ignored](#ignored)
2829
- [Reporting](#reporting)
2930
- [report to issue](#report-to-issue)
3031
- [report to email (TODO #12)](#report-to-email-todo-12)
@@ -166,6 +167,19 @@ The expected format of a target is `platform://path/to/your/group-or-project`
166167
For example:
167168
`--target gitlab://namespace/group --target github://organization/project`
168169

170+
##### ignored
171+
172+
| CLI options | File config |
173+
|---|---|
174+
| (repeatable) `--ignore` | `ignored` |
175+
176+
Sets the list of groups and projects to be ignored from scanning.
177+
Useful when you have a `target` which is a group, but you want to ignore a specific project from that group.
178+
The expected format of a target is `platform://path/to/your/group-or-project`
179+
180+
For example:
181+
`--ignore gitlab://namespace/group --ignore github://organization/project`
182+
169183
#### Reporting
170184

171185
##### report to issue

internal/cli/patrol.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
const configFlag = "config"
2828
const verboseFlag = "verbose"
2929
const targetFlag = "target"
30+
const ignoreFlag = "ignore"
3031
const reportToEmailFlag = "report-to-email"
3132
const reportToIssueFlag = "report-to-issue"
3233
const reportToSlackChannel = "report-to-slack-channel"
@@ -56,6 +57,11 @@ var PatrolFlags = []cli.Flag{
5657
Usage: "Groups and projects to scan for vulnerabilities (list argument which can be repeated)",
5758
Category: string(Scanning),
5859
},
60+
&cli.StringSliceFlag{
61+
Name: ignoreFlag,
62+
Usage: "List of repositories or groups to ignore (list argument which can be repeated)",
63+
Category: string(Scanning),
64+
},
5965
&cli.StringSliceFlag{
6066
Name: reportToEmailFlag,
6167
Usage: "Enable reporting to the provided list of emails",
@@ -110,6 +116,7 @@ func PatrolAction(cCtx *cli.Context) error {
110116
config, err := config.GetPatrolConfiguration(config.PatrolCLIOpts{
111117
PatrolCommonOpts: config.PatrolCommonOpts{
112118
Targets: getStringSliceIfSet(cCtx, targetFlag),
119+
Ignored: getStringSliceIfSet(cCtx, ignoreFlag),
113120
Report: config.PatrolReportOpts{
114121
To: config.PatrolReportToOpts{
115122
Issue: getBoolIfSet(cCtx, reportToIssueFlag),

internal/config/patrol.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type ProjectLocation struct {
1616

1717
type PatrolConfig struct {
1818
Locations []ProjectLocation
19+
Ignored []ProjectLocation
1920
ReportToEmails []string
2021
ReportToSlackChannels []string
2122
ReportToIssue bool
@@ -39,6 +40,7 @@ type PatrolReportOpts struct {
3940

4041
type PatrolCommonOpts struct {
4142
Targets *[]string `toml:"targets"`
43+
Ignored *[]string `toml:"ignored"`
4244
Report PatrolReportOpts `toml:"report"`
4345
}
4446

@@ -86,6 +88,12 @@ func mergeConfigs(cliOpts PatrolCLIOpts, fileOpts PatrolFileOpts) (config Patrol
8688
return config, errors.Join(errors.New("could not parse targets from CLI options"), err)
8789
}
8890

91+
ignored := getCliOrFileOption(cliOpts.Ignored, fileOpts.Ignored, []string{})
92+
parsedIgnored, err := parseTargets(ignored)
93+
if err != nil {
94+
return config, errors.Join(errors.New("could not parse targets from CLI options"), err)
95+
}
96+
8997
config = PatrolConfig{
9098
Locations: parsedLocations,
9199
ReportToIssue: getCliOrFileOption(cliOpts.Report.To.Issue, fileOpts.Report.To.Issue, false),
@@ -94,6 +102,7 @@ func mergeConfigs(cliOpts PatrolCLIOpts, fileOpts PatrolFileOpts) (config Patrol
94102
EnableProjectReportTo: getCliOrFileOption(cliOpts.Report.To.EnableProjectReportTo, fileOpts.Report.To.EnableProjectReportTo, false),
95103
SilentReport: getCliOrFileOption(cliOpts.Report.SilentReport, fileOpts.Report.SilentReport, false),
96104
Verbose: cliOpts.Verbose,
105+
Ignored: parsedIgnored,
97106
}
98107

99108
return

internal/config/patrol_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
func TestGetPatrolConfiguration(t *testing.T) {
1111
want := PatrolConfig{
1212
Locations: []ProjectLocation{{Type: repository.Gitlab, Path: "group1"}, {Type: repository.Gitlab, Path: "group2/project1"}},
13+
Ignored: []ProjectLocation{},
1314
ReportToEmails: []string{"[email protected]"},
1415
ReportToSlackChannels: []string{"report-slack-channel"},
1516
ReportToIssue: true,
@@ -30,6 +31,7 @@ func TestGetPatrolConfiguration(t *testing.T) {
3031
func TestGetPatrolConfigurationCLIOverridesFile(t *testing.T) {
3132
want := PatrolConfig{
3233
Locations: []ProjectLocation{{Type: repository.Gitlab, Path: "group1"}, {Type: repository.Gitlab, Path: "group2/project1"}},
34+
Ignored: []ProjectLocation{},
3335
ReportToEmails: []string{"[email protected]", "[email protected]"},
3436
ReportToSlackChannels: []string{"other-slack-channel"},
3537
ReportToIssue: false,

internal/config/project.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type ProjectConfig struct {
2525
Report ProjectReport `toml:"report"`
2626
SlackChannel string `toml:"slack-channel"` // TODO #27: Break in v1.0. Kept for backwards-compatibility
2727
Acknowledged []AcknowledgedVuln `toml:"acknowledged"`
28+
Ignored []string `toml:"ignored"` // List of repositories or groups to ignore
2829
}
2930

3031
func GetProjectConfiguration(projectName string, dir string) (config ProjectConfig) {

internal/patrol/patrol.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func New(repoService provider.IProvider, slackService slack.IService, osvService
4747

4848
// Patrol scans the given Gitlab groups and projects, creates and publishes the necessary reports.
4949
func (s *sheriffService) Patrol(args config.PatrolConfig) (warn error, err error) {
50-
scanReports, swarn, err := s.scanAndGetReports(args.Locations)
50+
scanReports, swarn, err := s.scanAndGetReports(args.Locations, args.Ignored)
5151
if err != nil {
5252
return nil, errors.Join(errors.New("failed to scan projects"), err)
5353
}
@@ -96,7 +96,7 @@ func (s *sheriffService) Patrol(args config.PatrolConfig) (warn error, err error
9696
return warn, nil
9797
}
9898

99-
func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation) (reports []scanner.Report, warn error, err error) {
99+
func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation, ignored []config.ProjectLocation) (reports []scanner.Report, warn error, err error) {
100100
// Create a temporary directory to store the scans
101101
err = os.MkdirAll(tempScanDir, os.ModePerm)
102102
if err != nil {
@@ -105,7 +105,7 @@ func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation) (
105105
defer os.RemoveAll(tempScanDir)
106106
log.Info().Str("path", tempScanDir).Msg("Created temporary directory")
107107

108-
projects, pwarn := s.getProjectList(locations)
108+
projects, pwarn := s.getProjectList(locations, ignored)
109109
if pwarn != nil {
110110
pwarn = errors.Join(errors.New("errors occured when getting project list"), pwarn)
111111
warn = errors.Join(pwarn, warn)
@@ -144,7 +144,18 @@ func (s *sheriffService) scanAndGetReports(locations []config.ProjectLocation) (
144144
return
145145
}
146146

147-
func (s *sheriffService) getProjectList(locs []config.ProjectLocation) (projects []repository.Project, warn error) {
147+
func (s *sheriffService) getProjectList(locs []config.ProjectLocation, ignored []config.ProjectLocation) (projects []repository.Project, warn error) {
148+
// Filter out locations that are in the ignored list
149+
locs = pie.Filter(locs, func(loc config.ProjectLocation) bool {
150+
return !slices.ContainsFunc(ignored, func(ignoredPath config.ProjectLocation) bool {
151+
ignore := ignoredPath.Path == loc.Path && ignoredPath.Type == loc.Type
152+
if ignore {
153+
log.Info().Str("path", loc.Path).Msg("Ignoring project location as it is in the ignored list")
154+
}
155+
return ignore
156+
})
157+
})
158+
148159
gitlabLocs := pie.Map(
149160
pie.Filter(locs, func(loc config.ProjectLocation) bool { return loc.Type == repository.Gitlab }),
150161
func(loc config.ProjectLocation) string { return loc.Path },

internal/patrol/patrol_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,25 @@ func TestMarkOutdatedAcknowledgements(t *testing.T) {
192192
assert.Equal(t, []string{"CVE-3"}, report.OutdatedAcks)
193193
}
194194

195+
func TestGetProjectList_IgnoresProject(t *testing.T) {
196+
mockClient := &mockClient{}
197+
mockClient.On("GetProjectList", []string{"group/to/scan"}).Return([]repository.Project{{Name: "Hello World", RepoUrl: "https://gitlab.com/group/to/scan.git", Repository: repository.Gitlab}}, nil)
198+
199+
mockRepoService := &mockRepoService{}
200+
mockRepoService.On("Provide", repository.Gitlab).Return(mockClient)
201+
202+
svc := New(mockRepoService, nil, nil)
203+
204+
// The ignored list contains the project path, so it should be filtered out
205+
projects, warn := svc.(*sheriffService).getProjectList(
206+
[]config.ProjectLocation{{Type: repository.Gitlab, Path: "group/to/scan"}},
207+
[]config.ProjectLocation{{Type: repository.Gitlab, Path: "group/to/scan"}},
208+
)
209+
assert.Nil(t, warn)
210+
assert.Empty(t, projects)
211+
mockClient.AssertNotCalled(t, "GetProjectList", []string{"group/to/scan"})
212+
}
213+
195214
type mockRepoService struct {
196215
mock.Mock
197216
}

0 commit comments

Comments
 (0)