From a96211dc33cefb3d89a478670e426a483a916791 Mon Sep 17 00:00:00 2001 From: Tom Petr Date: Thu, 20 Jul 2023 16:27:21 -0400 Subject: [PATCH] add github and gitlab config shortcuts (#30) --- README.md | 44 ++++++++++++++++++++- pkg/config.go | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e9f113..7cb0416 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,51 @@ inbound: allowlist: [...] ``` +### GitHub + +The `github` configuration section simplifies granting Semgrep access to leave PR comments. + +Example: +```yaml +inbound: + github: + baseUrl: https://github.example.com/api/v3 + token: ... +``` + +Under the hood, this config adds these allowlist items: + +- GET `https://github.example.com/api/v3/repos/:owner/:repo` +- GET `https://github.example.com/api/v3/repos/:owner/:repo/pulls` +- POST `https://github.example.com/api/v3/repos/:owner/:repo/pulls/:number/comments` +- POST `https://github.example.com/api/v3/repos/:owner/:repo/issues/:number/comments` + +### GitLab + +Similarly, the `gitlab` configuration section grants Semgrep access to leave MR comments. + +Example: +```yaml +inbound: + gitlab: + baseUrl: https://gitlab.example.com/api/v4 + token: ... +``` + +Under the hood, this config adds these allowlist items: + +- GET `https://gitlab.example.com/api/v4/projects/:project` +- GET `https://gitlab.example.com/api/v4/projects/:project/merge_requests` +- GET `https://gitlab.example.com/api/v4/projects/:project/merge_requests/:number/versions` +- GET `https://gitlab.example.com/api/v4/projects/:project/merge_requests/:number/discussions` +- POST `https://gitlab.example.com/api/v4/projects/:project/merge_requests/:number/discussions` +- PUT `https://gitlab.example.com/api/v4/projects/:project/merge_requests/:number/discussions/:discussion/notes/:note` +- PUT `https://gitlab.example.com/api/v4/projects/:project/merge_requests/:number/discussions/:discussion` + + ### Allowlist -The `allowlist` configuration section controls what HTTP requests are allowed to be forwarded out of the broker. The first matching allowlist item is used. No allowlist match means the request will not be proxied. +The `allowlist` configuration section provides finer-grained control over what HTTP requests are allowed to be forwarded out of the broker. The first matching allowlist item is used. No allowlist match means the request will not be proxied. Examples: ```yaml diff --git a/pkg/config.go b/pkg/config.go index 7af4746..e227fdf 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/url" "reflect" "strings" @@ -194,6 +195,16 @@ type UnsafeConfig struct { ForceHTTP bool `mapstructure:"forceHttp" json:"forceHttp"` } +type GitHub struct { + BaseURL string `mapstructure:"baseUrl" json:"baseUrl"` + Token string `mapstructure:"token" json:"token"` +} + +type GitLab struct { + BaseURL string `mapstructure:"baseUrl" json:"baseUrl"` + Token string `mapstructure:"token" json:"token"` +} + type InboundProxyConfig struct { Wireguard WireguardBase `mapstructure:"wireguard" json:"wireguard"` Allowlist Allowlist `mapstructure:"allowlist" json:"allowlist"` @@ -201,6 +212,8 @@ type InboundProxyConfig struct { Logging LoggingConfig `mapstructure:"logging" json:"logging"` Heartbeat HeartbeatConfig `mapstructure:"heartbeat" json:"heartbeat"` Unsafe UnsafeConfig `mapstructure:"unsafe" json:"unsafe"` + GitHub *GitHub `mapstructure:"github" json:"github"` + GitLab *GitLab `mapstructure:"gitlab" json:"gitlab"` } type FilteredRelayConfig struct { @@ -234,5 +247,97 @@ func LoadConfig(configFiles []string) (*Config, error) { return nil, fmt.Errorf("failed to unmarshal config: %v", err) } defaults.SetDefaults(config) + + if config.Inbound.GitHub != nil { + gitHub := config.Inbound.GitHub + + gitHubBaseUrl, err := url.Parse(gitHub.BaseURL) + if err != nil { + return nil, fmt.Errorf("failed to parse github base URL: %v", err) + } + + headers := map[string]string{ + "Authorization": fmt.Sprintf("Bearer %v", gitHub.Token), + } + + config.Inbound.Allowlist = append(config.Inbound.Allowlist, + // repo info + AllowlistItem{ + URL: gitHubBaseUrl.JoinPath("/repos/:owner/:repo").String(), + Methods: HttpMethods(MethodGet), + SetRequestHeaders: headers, + }, + // PR info + AllowlistItem{ + URL: gitHubBaseUrl.JoinPath("/repos/:owner/:repo/pulls").String(), + Methods: HttpMethods(MethodGet), + SetRequestHeaders: headers, + }, + // post PR comment + AllowlistItem{ + URL: gitHubBaseUrl.JoinPath("/repos/:owner/:repo/pulls/:number/comments").String(), + Methods: HttpMethods(MethodPost), + SetRequestHeaders: headers, + }, + // post issue comment + AllowlistItem{ + URL: gitHubBaseUrl.JoinPath("/repos/:owner/:repo/issues/:number/comments").String(), + Methods: HttpMethods(MethodPost), + SetRequestHeaders: headers, + }) + } + + if config.Inbound.GitLab != nil { + gitLab := config.Inbound.GitLab + + gitLabBaseUrl, err := url.Parse(gitLab.BaseURL) + if err != nil { + return nil, fmt.Errorf("failed to parse gitlab base URL: %v", err) + } + + headers := map[string]string{ + "PRIVATE-TOKEN": gitLab.Token, + } + + config.Inbound.Allowlist = append(config.Inbound.Allowlist, + // repo info + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project").String(), + Methods: HttpMethods(MethodGet), + SetRequestHeaders: headers, + }, + // MR info + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project/merge_requests").String(), + Methods: HttpMethods(MethodGet), + SetRequestHeaders: headers, + }, + // MR versions + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project/merge_requests/:number/versions").String(), + Methods: HttpMethods(MethodGet), + SetRequestHeaders: headers, + }, + // post MR comment + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project/merge_requests/:number/discussions").String(), + Methods: HttpMethods(MethodGet | MethodPost), + SetRequestHeaders: headers, + }, + // update MR comment + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project/merge_requests/:number/discussions/:discussion/notes/:note").String(), + Methods: HttpMethods(MethodPut), + SetRequestHeaders: headers, + }, + // resolve MR comment + AllowlistItem{ + URL: gitLabBaseUrl.JoinPath("/projects/:project/merge_requests/:number/discussions/:discussion").String(), + Methods: HttpMethods(MethodPut), + SetRequestHeaders: headers, + }, + ) + } + return config, nil }