Skip to content

Commit ea7d32d

Browse files
huntermorwn
andauthored
Add support for self-hosted SCM (#111)
* feat: SCM selection Adds support for specifying an SCM for supporting self-hosted environments. * fix: use path for GL repo name Matches the escaped name used in Github * chore: add results.json to gitignore * docs: updated readme with scm flag * fix: support for GH enterprise client * chore: optional Gitlab client BaseURL for SaaS * fix: change 'scm' flag to 'scm-platform' * fix: incorrect GitlabPlatform case Co-authored-by: Mor Weinberger <morwnbrg@gmail.com> * fix: gitlab toRepository test Co-authored-by: Mor Weinberger <morwnbrg@gmail.com>
1 parent 7b31054 commit ea7d32d

13 files changed

Lines changed: 82 additions & 42 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@
1919
/__debug_bin
2020

2121
.DS_Store
22-
/chain-bench
22+
/chain-bench
23+
/results.json

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ Get Chain-bench via your favorite installation method. See [installation] sectio
7979
chain-bench scan --repository-url <REPOSITORY_URL> --access-token <TOKEN> -o <OUTPUT_PATH>
8080
```
8181

82+
### Using Self-hosted or Dedicated SCM Platforms (with custom domains)
83+
84+
```bash
85+
chain-bench scan --repository-url <REPOSITORY_URL> --scm-platform <SCM_PLATFORM> --access-token <TOKEN> -o <OUTPUT_PATH>
86+
```
87+
88+
Supported options for `scm-platform` are `"github"` and `"gitlab"` (beta)
89+
8290
### Using docker
8391

8492
```bash
@@ -155,7 +163,7 @@ chain-bench-scanning:
155163
name: docker.io/aquasec/chain-bench
156164
entrypoint: [""]
157165
script:
158-
- chain-bench scan --repository-url $CI_PROJECT_URL --access-token $CHAIN_BENCH_TOKEN -o results.json --template @/templates/gitlab_security_scanner.tpl
166+
- chain-bench scan --repository-url $CI_PROJECT_URL --access-token $CHAIN_BENCH_TOKEN --scm-platform gitlab -o results.json --template @/templates/gitlab_security_scanner.tpl
159167
artifacts:
160168
reports:
161169
container_scanning: results.json

internal/commands/flags.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,16 @@ var (
4747
repositoryUrlFlagName = "repository-url"
4848
repositoryUrlShortFlag = "r"
4949

50-
// repositoryUrl the path to the repository to scan
50+
// accessToken the access token to use for the repository
5151
accessToken string
5252
accessTokenFlagName = "access-token"
5353
accessTokenShortFlag = "t"
5454

55+
// SCM platform for self-hosted/dedicated environments
56+
scmPlatform string
57+
scmPlatformFlagName = "scm-platform"
58+
scmPlatformShortFlag = "s"
59+
5560
// branch the branch name to scan
5661
branch string
5762
branchFlagName = "branch"

internal/commands/scan.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ func NewScanCommand() *cobra.Command {
1919
RunE: func(cmd *cobra.Command, args []string) error {
2020
start := time.Now()
2121
logger.Infof("%v Fetch Starting", emoji.TriangularFlag)
22-
assetsData, supportedChecks, err := clients.FetchClientData(accessToken, repositoryUrl, branch)
22+
23+
assetsData, supportedChecks, err := clients.FetchClientData(accessToken, repositoryUrl, scmPlatform, branch)
2324
if err != nil {
2425
logger.Error(err, "Failed to fetch client data")
2526
return err
@@ -46,9 +47,13 @@ func NewScanCommand() *cobra.Command {
4647
accessTokenFlagName, accessTokenShortFlag, "",
4748
"the access token to use for the repository")
4849

50+
scanCommand.PersistentFlags().StringVarP(&scmPlatform,
51+
scmPlatformFlagName, scmPlatformShortFlag, "",
52+
"the SCM platform for self-hosted/dedicated environments (options: 'github', 'gitlab')")
53+
4954
scanCommand.PersistentFlags().StringVarP(&branch,
5055
branchFlagName, branchShortFlag, "",
51-
"the branch to scan branch protection for")
56+
"the branch to scan for branch protection")
5257

5358
return scanCommand
5459
}

internal/scm-clients/adapter/adapter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type ClientAdapter interface {
11-
Init(client *http.Client, token string) error
11+
Init(client *http.Client, token string, host string) error
1212
ListSupportedChecksIDs() ([]string, error)
1313
GetRepository(owner, repo, branchName string) (*models.Repository, error)
1414
ListRepositoryBranches(owner, repo string) ([]*models.Branch, error)

internal/scm-clients/clients/clients.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,25 @@ import (
2020
const (
2121
GithubEndpoint = "github.com"
2222
GitlabEndpoint = "gitlab.com"
23+
24+
GithubPlatform = "github"
25+
GitlabPlatform = "gitlab"
2326
)
2427

25-
func FetchClientData(accessToken string, repoUrl string, branch string) (*checkmodels.AssetsData, []string, error) {
26-
scmName, orgName, repoName, err := getRepoInfo(repoUrl)
28+
func FetchClientData(accessToken string, repoUrl string, scmPlatform string, branch string) (*checkmodels.AssetsData, []string, error) {
29+
host, orgName, repoName, err := getRepoInfo(repoUrl)
2730
if err != nil {
2831
return nil, nil, err
2932
}
3033

31-
adapter, err := getClientAdapter(scmName, accessToken)
34+
switch host {
35+
case GithubEndpoint:
36+
scmPlatform = GithubPlatform
37+
case GitlabEndpoint:
38+
scmPlatform = GitlabPlatform
39+
}
40+
41+
adapter, err := getClientAdapter(scmPlatform, accessToken, host)
3242
if err != nil {
3343
return nil, nil, err
3444
}
@@ -87,31 +97,31 @@ func getRepoInfo(repoFullUrl string) (string, string, string, error) {
8797
if len(path) < 3 {
8898
return "", "", "", fmt.Errorf("missing org/repo in the repository url: %s", repoFullUrl)
8999
}
90-
repo := path[len(path) - 1]
100+
repo := path[len(path)-1]
91101
namespace := strings.Split(u.Path, repo)[0]
92102
trimedNamespace := namespace[1:(len(namespace) - 1)]
93103

94104
return u.Host, trimedNamespace, repo, nil
95105
}
96106

97-
func getClientAdapter(scmName string, accessToken string) (adapter.ClientAdapter, error) {
107+
func getClientAdapter(scmPlatform string, accessToken string, host string) (adapter.ClientAdapter, error) {
98108
var err error
99109
var adapter adapter.ClientAdapter
100110
httpClient := utils.GetHttpClient(accessToken)
101111

102-
switch scmName {
103-
case GithubEndpoint:
104-
err = github.Adapter.Init(httpClient, accessToken)
112+
switch scmPlatform {
113+
case GithubPlatform:
114+
err = github.Adapter.Init(httpClient, accessToken, host)
105115
adapter = &github.Adapter
106-
case GitlabEndpoint:
107-
err = gitlab.Adapter.Init(httpClient, accessToken)
116+
case GitlabPlatform:
117+
err = gitlab.Adapter.Init(httpClient, accessToken, host)
108118
adapter = &gitlab.Adapter
109119
default:
110120
adapter = nil
111121
}
112122

113123
if err != nil {
114-
logger.Error(err, "error with github init client")
124+
logger.Error(err, "error with SCM init client")
115125
return &github.ClientAdapterImpl{}, nil
116126
}
117127
return adapter, err

internal/scm-clients/github/adapter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ type ClientAdapterImpl struct {
2323
}
2424

2525
// Init implements clients.ClientAdapter
26-
func (*ClientAdapterImpl) Init(client *http.Client, token string) error {
27-
ghClient, err := InitClient(client, token)
26+
func (*ClientAdapterImpl) Init(client *http.Client, token string, host string) error {
27+
ghClient, err := InitClient(client, token, host)
2828
Adapter = ClientAdapterImpl{client: ghClient}
2929
return err
3030
}

internal/scm-clients/github/client_mocks.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func MockGetRepo(repo *github.Repository) *GithubClient {
1212
*repo,
1313
),
1414
)
15-
gc, _ := InitClient(mockedHTTPClient,"")
15+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
1616
return &gc
1717
}
1818

@@ -23,7 +23,7 @@ func MockGetBranchProtections(protection *github.Protection) *GithubClient {
2323
*protection,
2424
),
2525
)
26-
gc, _ := InitClient(mockedHTTPClient, "")
26+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
2727
return &gc
2828
}
2929

@@ -34,7 +34,7 @@ func MockGetSignaturesOfProtectedBranch(signedCommits *github.SignaturesProtecte
3434
*signedCommits,
3535
),
3636
)
37-
gc, _ := InitClient(mockedHTTPClient, "")
37+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
3838
return &gc
3939
}
4040

@@ -45,7 +45,7 @@ func MockGetOrganization(org *github.Organization) *GithubClient {
4545
*org,
4646
),
4747
)
48-
gc, _ := InitClient(mockedHTTPClient, "")
48+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
4949
return &gc
5050
}
5151

@@ -56,7 +56,7 @@ func MockGetOrganizationMembers(users []*github.User) *GithubClient {
5656
users,
5757
),
5858
)
59-
gc, _ := InitClient(mockedHTTPClient, "")
59+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
6060
return &gc
6161
}
6262

@@ -67,7 +67,7 @@ func MockGetWorkflows(workflows *github.Workflows) *GithubClient {
6767
workflows,
6868
),
6969
)
70-
gc, _ := InitClient(mockedHTTPClient, "")
70+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
7171
return &gc
7272
}
7373

@@ -78,7 +78,7 @@ func MockGetContent(content *github.RepositoryContent) *GithubClient {
7878
content,
7979
),
8080
)
81-
gc, _ := InitClient(mockedHTTPClient, "")
81+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
8282
return &gc
8383
}
8484

@@ -89,7 +89,7 @@ func MockGetOrganizationWebhooks(hooks []*github.Hook) *GithubClient {
8989
hooks,
9090
),
9191
)
92-
gc, _ := InitClient(mockedHTTPClient, "")
92+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
9393
return &gc
9494
}
9595

@@ -100,7 +100,7 @@ func MockGetRepositoryWebhooks(hooks []*github.Hook) *GithubClient {
100100
hooks,
101101
),
102102
)
103-
gc, _ := InitClient(mockedHTTPClient, "")
103+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
104104
return &gc
105105
}
106106

@@ -111,7 +111,7 @@ func MockListCommits(commits []*github.RepositoryCommit) *GithubClient {
111111
commits,
112112
),
113113
)
114-
gc, _ := InitClient(mockedHTTPClient, "")
114+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
115115
return &gc
116116
}
117117

@@ -122,6 +122,6 @@ func MockListOrganizationPackages(packages []*github.Package) *GithubClient {
122122
packages,
123123
),
124124
)
125-
gc, _ := InitClient(mockedHTTPClient, "")
125+
gc, _ := InitClient(mockedHTTPClient, "", "github.com")
126126
return &gc
127127
}

internal/scm-clients/github/github.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package github
22

33
import (
44
"context"
5+
"fmt"
56
"net/http"
67

78
"github.com/aquasecurity/chain-bench/internal/utils"
@@ -42,8 +43,14 @@ type GithubClientImpl struct {
4243

4344
var _ GithubClient = (*GithubClientImpl)(nil) // Verify that *GithubClientImpl implements GithubClient.
4445

45-
func InitClient(client *http.Client, token string) (GithubClient, error) {
46-
gc := github.NewClient(client)
46+
func InitClient(client *http.Client, token string, host string) (GithubClient, error) {
47+
var gc *github.Client
48+
if host == "github.com" {
49+
gc = github.NewClient(client)
50+
} else {
51+
gc, _ = github.NewEnterpriseClient(fmt.Sprintf("https://%s/api/v3/", host), fmt.Sprintf("https://%s/api/uploads/", host), client)
52+
}
53+
4754
Client = &GithubClientImpl{ctx: context.TODO(), client: gc}
4855
return Client, nil
4956
}
@@ -116,17 +123,17 @@ func (gca *GithubClientImpl) GetContent(owner, repo, filepath, ref string) (file
116123
return gca.client.Repositories.GetContents(gca.ctx, owner, repo, filepath, &github.RepositoryContentGetOptions{Ref: ref})
117124
}
118125

119-
//need admin:repo_hook->read:repo_hook
126+
// need admin:repo_hook->read:repo_hook
120127
func (gca *GithubClientImpl) ListRepositoryHooks(owner, repo string) (hooks []*github.Hook, resp *github.Response, err error) {
121128
return gca.client.Repositories.ListHooks(gca.ctx, owner, repo, nil)
122129
}
123130

124-
//need admin:org_hook
131+
// need admin:org_hook
125132
func (gca *GithubClientImpl) ListOrganizationHooks(owner string) (hooks []*github.Hook, resp *github.Response, err error) {
126133
return gca.client.Organizations.ListHooks(gca.ctx, owner, nil)
127134
}
128135

129-
//need read:packages
136+
// need read:packages
130137
func (gca *GithubClientImpl) ListOrganizationPackages(owner string, packageType string) ([]*github.Package, *github.Response, error) {
131138
return gca.client.Organizations.ListPackages(gca.ctx, owner, &github.PackageListOptions{State: utils.GetPtr("active"), PackageType: &packageType})
132139
}

internal/scm-clients/gitlab/adapter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type ClientAdapterImpl struct {
1919
client GitlabClient
2020
}
2121

22-
func (*ClientAdapterImpl) Init(client *http.Client, token string) error {
23-
glClient, err := InitClient(client, token)
22+
func (*ClientAdapterImpl) Init(client *http.Client, token string, host string) error {
23+
glClient, err := InitClient(client, token, host)
2424
Adapter = ClientAdapterImpl{client: glClient}
2525
return err
2626
}

0 commit comments

Comments
 (0)