Skip to content

Commit 337f52e

Browse files
committed
refactor(#32): move patrol and project config parsing to config package
1 parent 5138ac0 commit 337f52e

File tree

14 files changed

+187
-145
lines changed

14 files changed

+187
-145
lines changed

internal/cli/patrol.go

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ package cli
22

33
import (
44
"errors"
5-
"fmt"
6-
"net/url"
5+
"sheriff/internal/config"
76
"sheriff/internal/git"
87
"sheriff/internal/gitlab"
98
"sheriff/internal/patrol"
@@ -98,78 +97,45 @@ var PatrolFlags = []cli.Flag{
9897
}
9998

10099
func PatrolAction(cCtx *cli.Context) error {
101-
verbose := cCtx.Bool(verboseFlag)
102-
103-
// Parse options
104-
locations, err := parseUrls(cCtx.StringSlice(urlFlag))
100+
config, err := config.GetPatrolConfiguration(config.PatrolCLIOpts{
101+
Urls: cCtx.StringSlice(urlFlag),
102+
ReportToIssue: cCtx.Bool(reportToIssueFlag),
103+
ReportToEmails: cCtx.StringSlice(reportToEmailFlag),
104+
ReportToSlackChannel: cCtx.String(reportToSlackChannel),
105+
EnableProjectReportTo: cCtx.Bool(reportEnableProjectReportToFlag),
106+
SilentReport: cCtx.Bool(silentReportFlag),
107+
Verbose: cCtx.Bool(verboseFlag),
108+
})
105109
if err != nil {
106-
return errors.Join(errors.New("failed to parse `--url` options"), err)
110+
return errors.Join(errors.New("failed to get patrol configuration"), err)
107111
}
108112

113+
// Get tokens
114+
gitlabToken := cCtx.String(gitlabTokenFlag)
115+
slackToken := cCtx.String(slackTokenFlag)
116+
109117
// Create services
110-
gitlabService, err := gitlab.New(cCtx.String(gitlabTokenFlag))
118+
gitlabService, err := gitlab.New(gitlabToken)
111119
if err != nil {
112120
return errors.Join(errors.New("failed to create GitLab service"), err)
113121
}
114122

115-
slackService, err := slack.New(cCtx.String(slackTokenFlag), verbose)
123+
slackService, err := slack.New(slackToken, config.Verbose)
116124
if err != nil {
117125
return errors.Join(errors.New("failed to create Slack service"), err)
118126
}
119127

120-
gitService := git.New(cCtx.String(gitlabTokenFlag))
128+
gitService := git.New(gitlabToken)
121129
osvService := scanner.NewOsvScanner()
122130

123131
patrolService := patrol.New(gitlabService, slackService, gitService, osvService)
124132

125133
// Do the patrol
126-
if warn, err := patrolService.Patrol(
127-
patrol.PatrolArgs{
128-
Locations: locations,
129-
ReportToIssue: cCtx.Bool(reportToIssueFlag),
130-
ReportToEmails: cCtx.StringSlice(reportToEmailFlag),
131-
ReportToSlackChannel: cCtx.String(reportToSlackChannel),
132-
EnableProjectReportTo: cCtx.Bool(reportEnableProjectReportToFlag),
133-
SilentReport: cCtx.Bool(silentReportFlag),
134-
Verbose: verbose,
135-
},
136-
); err != nil {
134+
if warn, err := patrolService.Patrol(config); err != nil {
137135
return errors.Join(errors.New("failed to scan"), err)
138136
} else if warn != nil {
139137
return cli.Exit("Patrol was partially successful, some errors occurred. Check the logs for more information.", 1)
140138
}
141139

142140
return nil
143141
}
144-
145-
func parseUrls(uris []string) ([]patrol.ProjectLocation, error) {
146-
locations := make([]patrol.ProjectLocation, len(uris))
147-
for i, uri := range uris {
148-
parsed, err := url.Parse(uri)
149-
if err != nil || parsed == nil {
150-
return nil, errors.Join(fmt.Errorf("failed to parse uri"), err)
151-
}
152-
153-
if !parsed.IsAbs() {
154-
return nil, fmt.Errorf("url missing platform scheme %v", uri)
155-
}
156-
157-
if parsed.Scheme == string(patrol.Github) {
158-
return nil, fmt.Errorf("github is currently unsupported, but is on our roadmap 😃") // TODO #9
159-
} else if parsed.Scheme != string(patrol.Gitlab) {
160-
return nil, fmt.Errorf("unsupported platform %v", parsed.Scheme)
161-
}
162-
163-
path, err := url.JoinPath(parsed.Host, parsed.Path)
164-
if err != nil {
165-
return nil, fmt.Errorf("failed to join host and path %v", uri)
166-
}
167-
168-
locations[i] = patrol.ProjectLocation{
169-
Type: patrol.PlatformType(parsed.Scheme),
170-
Path: path,
171-
}
172-
}
173-
174-
return locations, nil
175-
}

internal/cli/patrol_test.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package cli
22

33
import (
44
"flag"
5-
"fmt"
6-
"sheriff/internal/patrol"
75
"testing"
86

97
"github.com/stretchr/testify/assert"
@@ -17,32 +15,3 @@ func TestPatrolActionEmptyRun(t *testing.T) {
1715

1816
assert.Nil(t, err)
1917
}
20-
21-
func TestParseUrls(t *testing.T) {
22-
testCases := []struct {
23-
paths []string
24-
wantProjectLocation *patrol.ProjectLocation
25-
wantError bool
26-
}{
27-
{[]string{"gitlab://namespace/project"}, &patrol.ProjectLocation{Type: "gitlab", Path: "namespace/project"}, false},
28-
{[]string{"gitlab://namespace/subgroup/project"}, &patrol.ProjectLocation{Type: "gitlab", Path: "namespace/subgroup/project"}, false},
29-
{[]string{"gitlab://namespace"}, &patrol.ProjectLocation{Type: "gitlab", Path: "namespace"}, false},
30-
{[]string{"github://organization"}, &patrol.ProjectLocation{Type: "github", Path: "organization"}, true},
31-
{[]string{"github://organization/project"}, &patrol.ProjectLocation{Type: "github", Path: "organization/project"}, true},
32-
{[]string{"unknown://namespace/project"}, nil, true},
33-
{[]string{"unknown://not a path"}, nil, true},
34-
{[]string{"not a url"}, nil, true},
35-
}
36-
37-
for _, tc := range testCases {
38-
urls, err := parseUrls(tc.paths)
39-
40-
fmt.Print(urls)
41-
42-
if tc.wantError {
43-
assert.NotNil(t, err)
44-
} else {
45-
assert.Equal(t, tc.wantProjectLocation, &(urls[0]))
46-
}
47-
}
48-
}

internal/config/patrol.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/url"
7+
)
8+
9+
type PlatformType string
10+
11+
const (
12+
Gitlab PlatformType = "gitlab"
13+
Github PlatformType = "github"
14+
)
15+
16+
type ProjectLocation struct {
17+
Type PlatformType
18+
Path string
19+
}
20+
21+
type PatrolConfig struct {
22+
Locations []ProjectLocation
23+
ReportToEmails []string
24+
ReportToSlackChannel string
25+
ReportToIssue bool
26+
EnableProjectReportTo bool
27+
SilentReport bool
28+
Verbose bool
29+
}
30+
31+
type PatrolCLIOpts struct {
32+
Urls []string
33+
ReportToEmails []string
34+
ReportToSlackChannel string
35+
ReportToIssue bool
36+
EnableProjectReportTo bool
37+
SilentReport bool
38+
Verbose bool
39+
}
40+
41+
func GetPatrolConfiguration(cliOpts PatrolCLIOpts) (patrolConfig PatrolConfig, err error) {
42+
// Parse options
43+
locations, err := parseUrls(cliOpts.Urls)
44+
if err != nil {
45+
return patrolConfig, errors.Join(errors.New("failed to parse `--url` options"), err)
46+
}
47+
48+
patrolConfig = PatrolConfig{
49+
Locations: locations,
50+
ReportToIssue: cliOpts.ReportToIssue,
51+
ReportToEmails: cliOpts.ReportToEmails,
52+
ReportToSlackChannel: cliOpts.ReportToSlackChannel,
53+
EnableProjectReportTo: cliOpts.EnableProjectReportTo,
54+
SilentReport: cliOpts.SilentReport,
55+
Verbose: cliOpts.Verbose,
56+
}
57+
58+
return
59+
}
60+
61+
func parseUrls(uris []string) ([]ProjectLocation, error) {
62+
locations := make([]ProjectLocation, len(uris))
63+
for i, uri := range uris {
64+
parsed, err := url.Parse(uri)
65+
if err != nil || parsed == nil {
66+
return nil, errors.Join(fmt.Errorf("failed to parse uri"), err)
67+
}
68+
69+
if !parsed.IsAbs() {
70+
return nil, fmt.Errorf("url missing platform scheme %v", uri)
71+
}
72+
73+
if parsed.Scheme == string(Github) {
74+
return nil, fmt.Errorf("github is currently unsupported, but is on our roadmap 😃") // TODO #9
75+
} else if parsed.Scheme != string(Gitlab) {
76+
return nil, fmt.Errorf("unsupported platform %v", parsed.Scheme)
77+
}
78+
79+
path, err := url.JoinPath(parsed.Host, parsed.Path)
80+
if err != nil {
81+
return nil, fmt.Errorf("failed to join host and path %v", uri)
82+
}
83+
84+
locations[i] = ProjectLocation{
85+
Type: PlatformType(parsed.Scheme),
86+
Path: path,
87+
}
88+
}
89+
90+
return locations, nil
91+
}

internal/config/patrol_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestParseUrls(t *testing.T) {
11+
testCases := []struct {
12+
paths []string
13+
wantProjectLocation *ProjectLocation
14+
wantError bool
15+
}{
16+
{[]string{"gitlab://namespace/project"}, &ProjectLocation{Type: "gitlab", Path: "namespace/project"}, false},
17+
{[]string{"gitlab://namespace/subgroup/project"}, &ProjectLocation{Type: "gitlab", Path: "namespace/subgroup/project"}, false},
18+
{[]string{"gitlab://namespace"}, &ProjectLocation{Type: "gitlab", Path: "namespace"}, false},
19+
{[]string{"github://organization"}, &ProjectLocation{Type: "github", Path: "organization"}, true},
20+
{[]string{"github://organization/project"}, &ProjectLocation{Type: "github", Path: "organization/project"}, true},
21+
{[]string{"unknown://namespace/project"}, nil, true},
22+
{[]string{"unknown://not a path"}, nil, true},
23+
{[]string{"not a url"}, nil, true},
24+
}
25+
26+
for _, tc := range testCases {
27+
urls, err := parseUrls(tc.paths)
28+
29+
fmt.Print(urls)
30+
31+
if tc.wantError {
32+
assert.NotNil(t, err)
33+
} else {
34+
assert.Equal(t, tc.wantProjectLocation, &(urls[0]))
35+
}
36+
}
37+
}

internal/config/project.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,26 @@ package config
33
import (
44
"errors"
55
"os"
6-
"sheriff/internal/scanner"
76

87
"github.com/BurntSushi/toml"
98
"github.com/elliotchance/pie/v2"
109
"github.com/rs/zerolog/log"
1110
)
1211

13-
func GetConfiguration(filename string) (config scanner.ProjectConfig, found bool, err error) {
12+
type AcknowledgedVuln struct {
13+
Code string `toml:"code"`
14+
Reason string `toml:"reason"`
15+
}
16+
17+
type ProjectConfig struct {
18+
ReportToSlackChannel string `toml:"report-to-slack-channel"`
19+
SlackChannel string `toml:"slack-channel"` // TODO #27: Break in v1.0. Kept for backwards-compatibility
20+
Acknowledged []AcknowledgedVuln `toml:"acknowledged"`
21+
}
22+
23+
func GetConfiguration(filename string) (config ProjectConfig, found bool, err error) {
1424
if _, err := os.Stat(filename); os.IsNotExist(err) {
15-
return scanner.ProjectConfig{}, false, nil
25+
return ProjectConfig{}, false, nil
1626
} else if err != nil {
1727
return config, false, errors.Join(errors.New("unexpected error when attempting to get project configuration"), err)
1828
}

internal/config/project_test.go

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

33
import (
4-
"sheriff/internal/scanner"
54
"testing"
65

76
"github.com/stretchr/testify/assert"
@@ -12,13 +11,13 @@ func TestGetConfiguration(t *testing.T) {
1211
filename string
1312
wantFound bool
1413
wantErr bool
15-
wantConfig scanner.ProjectConfig
14+
wantConfig ProjectConfig
1615
}{
17-
{"testdata/valid.toml", true, false, scanner.ProjectConfig{ReportToSlackChannel: "the-devils-slack-channel"}},
18-
{"testdata/invalid.toml", true, true, scanner.ProjectConfig{}},
19-
{"testdata/nonexistent.toml", false, false, scanner.ProjectConfig{}},
20-
{"testdata/valid_with_ack.toml", true, false, scanner.ProjectConfig{Acknowledged: []scanner.AcknowledgedVuln{{Code: "CSV111", Reason: "not relevant"}, {Code: "CSV222", Reason: ""}}}},
21-
{"testdata/valid_with_ack_alt.toml", true, false, scanner.ProjectConfig{Acknowledged: []scanner.AcknowledgedVuln{{Code: "CSV111", Reason: "not relevant"}, {Code: "CSV222", Reason: ""}}}},
16+
{"testdata/valid.toml", true, false, ProjectConfig{ReportToSlackChannel: "the-devils-slack-channel"}},
17+
{"testdata/invalid.toml", true, true, ProjectConfig{}},
18+
{"testdata/nonexistent.toml", false, false, ProjectConfig{}},
19+
{"testdata/valid_with_ack.toml", true, false, ProjectConfig{Acknowledged: []AcknowledgedVuln{{Code: "CSV111", Reason: "not relevant"}, {Code: "CSV222", Reason: ""}}}},
20+
{"testdata/valid_with_ack_alt.toml", true, false, ProjectConfig{Acknowledged: []AcknowledgedVuln{{Code: "CSV111", Reason: "not relevant"}, {Code: "CSV222", Reason: ""}}}},
2221
}
2322

2423
for _, tc := range testCases {
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)