Skip to content

Commit cd0bc0b

Browse files
committed
feat(#32): improve configuration API
Changes urls -> targets Changes slack-channel -> slack-channels Adds readme with details of all configuration options
1 parent 92091eb commit cd0bc0b

File tree

8 files changed

+149
-43
lines changed

8 files changed

+149
-43
lines changed

README.md

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@ Sheriff is a tool to scan repositories and generate security reports.
1919
- [CLI flags](#cli-flags)
2020
- [Environment variables](#environment-variables)
2121
- [Configuration file](#configuration-file)
22+
- [Configuration options](#configuration-options)
23+
- [Miscellaneous](#miscellaneous)
24+
- [config](#config)
25+
- [verbose](#verbose)
26+
- [Scanning](#scanning)
27+
- [targets](#targets)
28+
- [Reporting](#reporting)
29+
- [report to issue](#report-to-issue)
30+
- [report to email (TODO #12)](#report-to-email-todo-12)
31+
- [report to slack channels](#report-to-slack-channels)
32+
- [enable project report to](#enable-project-report-to)
33+
- [silent](#silent)
34+
- [Tokens](#tokens)
35+
- [gitlab token](#gitlab-token)
36+
- [slack token](#slack-token)
2237
- [Supported platforms](#supported-platforms)
2338
- [Source code hosting services](#source-code-hosting-services)
2439
- [Messaging services](#messaging-services)
@@ -28,7 +43,7 @@ Sheriff is a tool to scan repositories and generate security reports.
2843
## Quick Usage
2944

3045
```sh
31-
sheriff patrol --url gitlab://your-namespace-or-group --report-to-issue
46+
sheriff patrol --target gitlab://your-namespace-or-group --report-to-issue
3247
```
3348

3449
## How it works
@@ -104,16 +119,109 @@ Only the **Reporting** and **Scanning** sections of configuration parameters are
104119
In this case you may choose to create a config file such as the following:
105120

106121
```toml
107-
url = ["namespace/group", "namespace/group/cool-repo"]
108-
report-to-slack-channel = "sheriff-report-test"
109-
report-to-gitlab-issue = true
122+
targets = ["namespace/group", "namespace/group/cool-repo"]
123+
[report.to]
124+
slack-channel = "sheriff-report-test"
125+
issue = true
110126
```
111127

112128
And if you wish to specify a different file, you can do so with `sheriff patrol --config your-config-file.toml`.
113129

114130
> [!NOTE]
115131
> When using several types of configurations at once there is an order of preference: **cli flags** > **env vars** > **config file**
116132
133+
### Configuration options
134+
135+
#### Miscellaneous
136+
137+
##### config
138+
139+
| CLI options | File config |
140+
|---|---|
141+
| `--config` | - |
142+
143+
Sets the path of your sheriff configuration file
144+
145+
##### verbose
146+
147+
| CLI options | File config |
148+
|---|---|
149+
| `--verbose`/`-v` | - |
150+
151+
Sets the log level to verbose
152+
153+
#### Scanning
154+
155+
##### targets
156+
157+
| CLI options | File config |
158+
|---|---|
159+
| (repeatable) `--target` | `targets` |
160+
161+
Sets the list of groups and projects to be scanned.
162+
The expected format of a target is `platform://path/to/your/group-or-project`
163+
164+
For example:
165+
`--target gitlab://namespace/group --target github://organization/project`
166+
167+
#### Reporting
168+
169+
##### report to issue
170+
171+
| CLI options | File config |
172+
|---|---|
173+
| `--report-to-issue` | <code>[report.to]<br>issue</code> |
174+
175+
Enables reporting to an issue on the project's platform
176+
177+
##### report to email (TODO #12)
178+
179+
| CLI options | File config |
180+
|---|---|
181+
| (repeatable) `--report-to-email` | <code>[report.to]<br>emails</code> |
182+
183+
Sets the list of email to which a full scan report should be sent
184+
185+
##### report to slack channels
186+
187+
| CLI options | File config |
188+
|---|---|
189+
| (repeatable) `--report-to-slack-channels` | <code>[report.to]<br>slack-channels</code> |
190+
191+
##### enable project report to
192+
193+
| CLI options | File config |
194+
|---|---|
195+
| `--report-to-enable-project-report-to` | <code>[report.to]<br>enable-project-report-to</code> |
196+
197+
Enable project-level configuration `report-to` to allow projects to control where their individual reports are sent
198+
199+
##### silent
200+
201+
| CLI options | File config |
202+
|---|---|
203+
| `--report-silent` | <code>[report]<br>silent</code> |
204+
205+
Disable printing the report in the bash output
206+
207+
#### Tokens
208+
209+
##### gitlab token
210+
211+
| ENV VAR |
212+
|---|
213+
| `$GITLAB_TOKEN` |
214+
215+
Sets the token to be used when fetching projects from gitlab
216+
217+
##### slack token
218+
219+
| ENV VAR |
220+
|---|
221+
| `$SLACK_TOKEN` |
222+
223+
Sets the token to be used when reporting the security report on slack
224+
117225
## Supported platforms
118226

119227
### Source code hosting services

internal/cli/patrol.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const (
2626

2727
const configFlag = "config"
2828
const verboseFlag = "verbose"
29-
const urlFlag = "url"
29+
const targetFlag = "target"
3030
const reportToEmailFlag = "report-to-email"
3131
const reportToIssueFlag = "report-to-issue"
3232
const reportToSlackChannel = "report-to-slack-channel"
@@ -51,7 +51,7 @@ var PatrolFlags = []cli.Flag{
5151
Value: false,
5252
},
5353
&cli.StringSliceFlag{
54-
Name: urlFlag,
54+
Name: targetFlag,
5555
Usage: "Groups and projects to scan for vulnerabilities (list argument which can be repeated)",
5656
Category: string(Scanning),
5757
},
@@ -65,9 +65,9 @@ var PatrolFlags = []cli.Flag{
6565
Usage: "Enable or disable reporting to the project's issue on the associated platform (gitlab, github, ...)",
6666
Category: string(Reporting),
6767
},
68-
&cli.StringFlag{
68+
&cli.StringSliceFlag{
6969
Name: reportToSlackChannel,
70-
Usage: "Enable reporting to the provided slack channel",
70+
Usage: "Enable reporting to the provided slack channels",
7171
Category: string(Reporting),
7272
},
7373
&cli.BoolFlag{
@@ -101,12 +101,12 @@ var PatrolFlags = []cli.Flag{
101101
func PatrolAction(cCtx *cli.Context) error {
102102
config, err := config.GetPatrolConfiguration(config.PatrolCLIOpts{
103103
PatrolCommonOpts: config.PatrolCommonOpts{
104-
Urls: getStringSliceIfSet(cCtx, urlFlag),
104+
Targets: getStringSliceIfSet(cCtx, targetFlag),
105105
Report: config.PatrolReportOpts{
106106
To: config.PatrolReportToOpts{
107107
Issue: getBoolIfSet(cCtx, reportToIssueFlag),
108108
Emails: getStringSliceIfSet(cCtx, reportToEmailFlag),
109-
SlackChannel: getStringIfSet(cCtx, reportToSlackChannel),
109+
SlackChannels: getStringSliceIfSet(cCtx, reportToSlackChannel),
110110
EnableProjectReportTo: getBoolIfSet(cCtx, reportEnableProjectReportToFlag),
111111
},
112112
SilentReport: getBoolIfSet(cCtx, silentReportFlag),

internal/config/patrol.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type ProjectLocation struct {
2424
type PatrolConfig struct {
2525
Locations []ProjectLocation
2626
ReportToEmails []string
27-
ReportToSlackChannel string
27+
ReportToSlackChannels []string
2828
ReportToIssue bool
2929
EnableProjectReportTo bool
3030
SilentReport bool
@@ -34,7 +34,7 @@ type PatrolConfig struct {
3434
// Options common in both the CLI options & file options
3535
type PatrolReportToOpts struct {
3636
Emails *[]string `toml:"emails"`
37-
SlackChannel *string `toml:"slack-channel"`
37+
SlackChannels *[]string `toml:"slack-channels"`
3838
Issue *bool `toml:"issue"`
3939
EnableProjectReportTo *bool `toml:"enable-project-report-to"`
4040
}
@@ -45,8 +45,8 @@ type PatrolReportOpts struct {
4545
}
4646

4747
type PatrolCommonOpts struct {
48-
Urls *[]string `toml:"urls"`
49-
Report PatrolReportOpts `toml:"report"`
48+
Targets *[]string `toml:"targets"`
49+
Report PatrolReportOpts `toml:"report"`
5050
}
5151

5252
// Options only available from CLI configuration
@@ -86,17 +86,17 @@ func GetPatrolConfiguration(cliOpts PatrolCLIOpts) (config PatrolConfig, err err
8686
}
8787

8888
func mergeConfigs(cliOpts PatrolCLIOpts, fileOpts PatrolFileOpts) (config PatrolConfig, err error) {
89-
locations := getCliOrFileOption(cliOpts.Urls, fileOpts.Urls, []string{})
90-
parsedLocations, err := parseUrls(locations)
89+
locations := getCliOrFileOption(cliOpts.Targets, fileOpts.Targets, []string{})
90+
parsedLocations, err := parseTargets(locations)
9191
if err != nil {
92-
return config, errors.Join(errors.New("could not parse urls from CLI options"), err)
92+
return config, errors.Join(errors.New("could not parse targets from CLI options"), err)
9393
}
9494

9595
config = PatrolConfig{
9696
Locations: parsedLocations,
9797
ReportToIssue: getCliOrFileOption(cliOpts.Report.To.Issue, fileOpts.Report.To.Issue, false),
9898
ReportToEmails: getCliOrFileOption(cliOpts.Report.To.Emails, fileOpts.Report.To.Emails, []string{}),
99-
ReportToSlackChannel: getCliOrFileOption(cliOpts.Report.To.SlackChannel, fileOpts.Report.To.SlackChannel, ""),
99+
ReportToSlackChannels: getCliOrFileOption(cliOpts.Report.To.SlackChannels, fileOpts.Report.To.SlackChannels, []string{}),
100100
EnableProjectReportTo: getCliOrFileOption(cliOpts.Report.To.EnableProjectReportTo, fileOpts.Report.To.EnableProjectReportTo, false),
101101
SilentReport: getCliOrFileOption(cliOpts.Report.SilentReport, fileOpts.Report.SilentReport, false),
102102
Verbose: cliOpts.Verbose,
@@ -118,16 +118,16 @@ func getCliOrFileOption[T interface{}](valueA *T, valueB *T, def T) (r T) {
118118
return def
119119
}
120120

121-
func parseUrls(uris []string) ([]ProjectLocation, error) {
122-
locations := make([]ProjectLocation, len(uris))
123-
for i, uri := range uris {
124-
parsed, err := url.Parse(uri)
121+
func parseTargets(targets []string) ([]ProjectLocation, error) {
122+
locations := make([]ProjectLocation, len(targets))
123+
for i, t := range targets {
124+
parsed, err := url.Parse(t)
125125
if err != nil || parsed == nil {
126126
return nil, errors.Join(fmt.Errorf("failed to parse uri"), err)
127127
}
128128

129129
if !parsed.IsAbs() {
130-
return nil, fmt.Errorf("url missing platform scheme %v", uri)
130+
return nil, fmt.Errorf("target missing platform scheme %v", t)
131131
}
132132

133133
if parsed.Scheme == string(Github) {
@@ -138,7 +138,7 @@ func parseUrls(uris []string) ([]ProjectLocation, error) {
138138

139139
path, err := url.JoinPath(parsed.Host, parsed.Path)
140140
if err != nil {
141-
return nil, fmt.Errorf("failed to join host and path %v", uri)
141+
return nil, fmt.Errorf("failed to join host and path %v", t)
142142
}
143143

144144
locations[i] = ProjectLocation{

internal/config/patrol_test.go

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

33
import (
4-
"fmt"
54
"testing"
65

76
"github.com/stretchr/testify/assert"
@@ -11,7 +10,7 @@ func TestGetPatrolConfiguration(t *testing.T) {
1110
want := PatrolConfig{
1211
Locations: []ProjectLocation{{Type: Gitlab, Path: "group1"}, {Type: Gitlab, Path: "group2/project1"}},
1312
ReportToEmails: []string{"[email protected]"},
14-
ReportToSlackChannel: "report-slack-channel",
13+
ReportToSlackChannels: []string{"report-slack-channel"},
1514
ReportToIssue: true,
1615
EnableProjectReportTo: true,
1716
SilentReport: true,
@@ -31,7 +30,7 @@ func TestGetPatrolConfigurationCLIOverridesFile(t *testing.T) {
3130
want := PatrolConfig{
3231
Locations: []ProjectLocation{{Type: Gitlab, Path: "group1"}, {Type: Gitlab, Path: "group2/project1"}},
3332
ReportToEmails: []string{"[email protected]", "[email protected]"},
34-
ReportToSlackChannel: "other-slack-channel",
33+
ReportToSlackChannels: []string{"other-slack-channel"},
3534
ReportToIssue: false,
3635
EnableProjectReportTo: false, // Here we test overriding with a zero-value, which works!
3736
SilentReport: false,
@@ -42,11 +41,11 @@ func TestGetPatrolConfigurationCLIOverridesFile(t *testing.T) {
4241
Config: "testdata/patrol/valid.toml",
4342
Verbose: true,
4443
PatrolCommonOpts: PatrolCommonOpts{
45-
Urls: &[]string{"gitlab://group1", "gitlab://group2/project1"},
44+
Targets: &[]string{"gitlab://group1", "gitlab://group2/project1"},
4645
Report: PatrolReportOpts{
4746
To: PatrolReportToOpts{
4847
Emails: &want.ReportToEmails,
49-
SlackChannel: &want.ReportToSlackChannel,
48+
SlackChannels: &want.ReportToSlackChannels,
5049
Issue: &want.ReportToIssue,
5150
EnableProjectReportTo: &want.EnableProjectReportTo,
5251
},
@@ -90,18 +89,16 @@ func TestParseUrls(t *testing.T) {
9089
{[]string{"github://organization/project"}, &ProjectLocation{Type: "github", Path: "organization/project"}, true},
9190
{[]string{"unknown://namespace/project"}, nil, true},
9291
{[]string{"unknown://not a path"}, nil, true},
93-
{[]string{"not a url"}, nil, true},
92+
{[]string{"not a target"}, nil, true},
9493
}
9594

9695
for _, tc := range testCases {
97-
urls, err := parseUrls(tc.paths)
98-
99-
fmt.Print(urls)
96+
targets, err := parseTargets(tc.paths)
10097

10198
if tc.wantError {
10299
assert.NotNil(t, err)
103100
} else {
104-
assert.Equal(t, tc.wantProjectLocation, &(urls[0]))
101+
assert.Equal(t, tc.wantProjectLocation, &(targets[0]))
105102
}
106103
}
107104
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
urls = ["gitlab://group1", "gitlab://group2/project1"]
1+
targets = ["gitlab://group1", "gitlab://group2/project1"]
22

33
[report]
44
silent = true
55

66
[report.to]
77
emails = ["[email protected]"]
8-
slack-channel = "report-slack-channel"
8+
slack-channels = ["report-slack-channel"]
99
issue = true
1010
enable-project-report-to = true

internal/patrol/patrol.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ func (s *sheriffService) Patrol(args config.PatrolConfig) (warn error, err error
7373
}
7474

7575
if s.slackService != nil {
76-
if args.ReportToSlackChannel != "" {
77-
log.Info().Str("slackChannel", args.ReportToSlackChannel).Msg("Posting report to slack channel")
76+
if len(args.ReportToSlackChannels) > 0 {
77+
// TODO #36 support sending to multiple slack channels (for now only the first is sent)
78+
log.Info().Str("slackChannel", args.ReportToSlackChannels[0]).Msg("Posting report to slack channel")
7879

7980
paths := pie.Map(args.Locations, func(v config.ProjectLocation) string { return v.Path })
80-
if err := publish.PublishAsGeneralSlackMessage(args.ReportToSlackChannel, scanReports, paths, s.slackService); err != nil {
81+
if err := publish.PublishAsGeneralSlackMessage(args.ReportToSlackChannels[0], scanReports, paths, s.slackService); err != nil {
8182
log.Error().Err(err).Msg("Failed to post slack report")
8283
err = errors.Join(errors.New("failed to post slack report"), err)
8384
warn = errors.Join(err, warn)

internal/patrol/patrol_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestScanNoProjects(t *testing.T) {
3434
warn, err := svc.Patrol(config.PatrolConfig{
3535
Locations: []config.ProjectLocation{{Type: config.Gitlab, Path: "group/to/scan"}},
3636
ReportToEmails: []string{},
37-
ReportToSlackChannel: "channel",
37+
ReportToSlackChannels: []string{"channel"},
3838
ReportToIssue: true,
3939
EnableProjectReportTo: true,
4040
Verbose: true,
@@ -67,7 +67,7 @@ func TestScanNonVulnerableProject(t *testing.T) {
6767
warn, err := svc.Patrol(config.PatrolConfig{
6868
Locations: []config.ProjectLocation{{Type: config.Gitlab, Path: "group/to/scan"}},
6969
ReportToEmails: []string{},
70-
ReportToSlackChannel: "channel",
70+
ReportToSlackChannels: []string{"channel"},
7171
ReportToIssue: true,
7272
EnableProjectReportTo: true,
7373
Verbose: true,
@@ -108,7 +108,7 @@ func TestScanVulnerableProject(t *testing.T) {
108108
warn, err := svc.Patrol(config.PatrolConfig{
109109
Locations: []config.ProjectLocation{{Type: config.Gitlab, Path: "group/to/scan"}},
110110
ReportToEmails: []string{},
111-
ReportToSlackChannel: "channel",
111+
ReportToSlackChannels: []string{"channel"},
112112
ReportToIssue: true,
113113
EnableProjectReportTo: true,
114114
Verbose: true,

internal/publish/to_slack.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func formatSummary(reportsBySeverityKind map[scanner.SeverityScoreKind][]scanner
140140
true, false,
141141
),
142142
)
143-
subtitleGroups := formatSubtitleList("urls", paths)
143+
subtitleGroups := formatSubtitleList("targets", paths)
144144
subtitleCount := goslack.NewContextBlock("subtitleCount", goslack.NewTextBlockObject("mrkdwn", fmt.Sprintf("Total projects scanned: %v", totalReports), false, false))
145145

146146
counts := pie.Map(severityScoreOrder, func(kind scanner.SeverityScoreKind) *goslack.TextBlockObject {

0 commit comments

Comments
 (0)