Skip to content

Commit

Permalink
Added upload code scanning to GitHub (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
omerzi committed Jul 28, 2022
1 parent ec12517 commit a1f65b2
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 24 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Currently supported providers are: [GitHub](#github), [Bitbucket Server](#bitbuc
- [Get a label](#get-a-label)
- [List Pull Request Labels](#list-pull-request-labels)
- [Unlabel Pull Request](#unlabel-pull-request)
- [Upload Code Scanning](#upload-code-scanning)
- [Webhook Parser](#webhook-parser)

### VCS Clients
Expand Down Expand Up @@ -454,6 +455,26 @@ pullRequestID := 5
err := client.UnlabelPullRequest(ctx, owner, repository, name, pullRequestID)
```

#### Upload Code Scanning

Notice - Code Scanning is currently supported on GitHub only.

```go
// Go context
ctx := context.Background()
// The account owner of the git repository
owner := "user"
// The name of the repository
repo := "my_repo"
// The branch name for which the code scanning is relevant
branch := "my_branch"
// A string representing the code scanning results
scanResults := "results"

// Uploads the scanning analysis file to the relevant git provider
sarifID, err := client.UploadCodeScanning(ctx, owner, repo, branch, scanResults)
```

### Webhook Parser

```go
Expand All @@ -469,3 +490,4 @@ provider := vcsutils.GitHub

webhookInfo, err := webhookparser.ParseIncomingWebhook(provider, token, request)
```

11 changes: 6 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ go 1.17

require (
github.com/gfleury/go-bitbucket-v1 v0.0.0-20220418082332-711d7d5e805f
github.com/google/go-github/v41 v41.0.0
github.com/google/go-github/v45 v45.2.0
github.com/google/uuid v1.3.0
github.com/grokify/mogo v0.39.8
github.com/ktrysmt/go-bitbucket v0.9.32
github.com/mitchellh/mapstructure v1.4.3
github.com/stretchr/testify v1.7.0
Expand All @@ -15,15 +16,15 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.25.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
74 changes: 64 additions & 10 deletions go.sum

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions vcsclient/bitbucketcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ func (client *BitbucketCloudClient) UnlabelPullRequest(ctx context.Context, owne
return errLabelsNotSupported
}

func (client *BitbucketCloudClient) UploadCodeScanning(ctx context.Context, owner string, repository string, branch string, scanResults string) (string, error) {
return "", errBitbucketCodeScanningNotSupported
}

func extractCommitFromResponse(commits interface{}) (*commitResponse, error) {
var res commitResponse
err := extractStructFromResponse(commits, &res)
Expand Down
1 change: 1 addition & 0 deletions vcsclient/bitbucketcommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
)

var errLabelsNotSupported = errors.New("labels are not supported on Bitbucket")
var errBitbucketCodeScanningNotSupported = errors.New("code scanning is not supported on Bitbucket")

func getBitbucketCommitState(commitState CommitStatus) string {
switch commitState {
Expand Down
4 changes: 4 additions & 0 deletions vcsclient/bitbucketserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,7 @@ func (client *BitbucketServerClient) mapBitbucketServerCommitToCommitInfo(commit
ParentHashes: parents,
}
}

func (client *BitbucketServerClient) UploadCodeScanning(ctx context.Context, owner string, repository string, branch string, scanResults string) (string, error) {
return "", errBitbucketCodeScanningNotSupported
}
62 changes: 58 additions & 4 deletions vcsclient/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package vcsclient

import (
"context"
"encoding/json"
"github.com/google/go-github/v45/github"
"github.com/grokify/mogo/encoding/base64"
"github.com/jfrog/froggit-go/vcsutils"
"golang.org/x/oauth2"
"net/http"
"net/url"
"strconv"
"strings"

"github.com/google/go-github/v41/github"
"github.com/jfrog/froggit-go/vcsutils"
"golang.org/x/oauth2"
)

// GitHubClient API version 3
Expand Down Expand Up @@ -441,6 +442,50 @@ func (client *GitHubClient) UnlabelPullRequest(ctx context.Context, owner, repos
return err
}

// UploadCodeScanning to GitHub Security tab
func (client *GitHubClient) UploadCodeScanning(ctx context.Context, owner, repository, branch, scanResults string) (string, error) {
packagedScan, err := packScanningResult(scanResults)
if err != nil {
return "", err
}
commit, err := client.GetLatestCommit(ctx, owner, repository, branch)
if err != nil {
return "", err
}
commitSHA := commit.Hash
ref := "refs/heads/" + branch
ghClient, err := client.buildGithubClient(ctx)
if err != nil {
return "", err
}
sarifID, resp, err := ghClient.CodeScanning.UploadSarif(ctx, owner, repository, &github.SarifAnalysis{
CommitSHA: &commitSHA,
Ref: &ref,
Sarif: &packagedScan,
CheckoutURI: nil,
})
// According to go-github API - successful response will return 202 status code
// The body of the response will appear in the error, and the Sarif struct will be empty.
if err != nil && resp.Response.StatusCode != 202 {
return "", err
}
// We are still using the Sarif struct because we need it for the unit-test of this function
if sarifID != nil && *sarifID.ID != "" {
return *sarifID.ID, err
}
aerr, ok := err.(*github.AcceptedError)
var result map[string]string
if ok {
err = json.Unmarshal(aerr.Raw, &result)
if err != nil {
return "", nil
}
return result["id"], nil
}

return "", nil
}

func createGitHubHook(token, payloadURL string, webhookEvents ...vcsutils.WebhookEvent) *github.Hook {
return &github.Hook{
Events: getGitHubWebhookEvents(webhookEvents...),
Expand Down Expand Up @@ -524,3 +569,12 @@ func mapGitHubPullRequestToPullRequestInfoList(pullRequestList []*github.PullReq
}
return
}

func packScanningResult(data string) (string, error) {
compressedScan, err := base64.EncodeGzip([]byte(data), 6)
if err != nil {
return "", err
}

return compressedScan, err
}
52 changes: 50 additions & 2 deletions vcsclient/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

"github.com/stretchr/testify/require"

"github.com/google/go-github/v41/github"
"github.com/google/go-github/v45/github"
"github.com/jfrog/froggit-go/vcsutils"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -534,6 +534,24 @@ func TestGitHubClient_UnlabelPullRequest(t *testing.T) {
assert.Error(t, err)
}

func TestGitHubClient_UploadScanningAnalysis(t *testing.T) {
ctx := context.Background()
scan := "{\n \"version\": \"2.1.0\",\n \"$schema\": \"https://json.schemastore.org/sarif-2.1.0-rtm.5.json\",\n \"runs\": [\n {\n \"tool\": {\n \"driver\": {\n \"informationUri\": \"https://jfrog.com/xray/\",\n \"name\": \"Xray\",\n \"rules\": [\n {\n \"id\": \"XRAY-174176\",\n \"shortDescription\": null,\n \"fullDescription\": {\n \"text\": \"json Package for Node.js lib/json.js _parseString() Function -d Argument Handling Local Code Execution Weakness\"\n },\n \"properties\": {\n \"security-severity\": \"8\"\n }\n }\n ]\n }\n },\n \"results\": [\n {\n \"ruleId\": \"XRAY-174176\",\n \"ruleIndex\": 1,\n \"message\": {\n \"text\": \"json 9.0.6. Fixed in Versions: [11.0.0]\"\n },\n \"locations\": [\n {\n \"physicalLocation\": {\n \"artifactLocation\": {\n \"uri\": \"package.json\"\n }\n }\n }\n ]\n }\n ]\n }\n ]\n }"
response, err := os.ReadFile(filepath.Join("testdata", "github", "commit_list_response.json"))
assert.NoError(t, err)
expectedUploadSarifID := "b16b0368-01b9-11ed-90a3-cabff0b8ad31"
client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, response,
fmt.Sprintf("/repos/%s/%s/commits?page=1&per_page=1&sha=master", owner, repo1), createGitHubSarifUploadHandler)
defer cleanUp()

sarifID, err := client.UploadCodeScanning(ctx, owner, repo1, "master", scan)
assert.NoError(t, err)
assert.Equal(t, expectedUploadSarifID, sarifID)

_, err = createBadGitHubClient(t).UploadCodeScanning(ctx, owner, repo1, "master", scan)
assert.Error(t, err)
}

func createBadGitHubClient(t *testing.T) VcsClient {
client, err := NewClientBuilder(vcsutils.GitHub).ApiEndpoint("https://bad^endpoint").Build()
require.NoError(t, err)
Expand All @@ -558,7 +576,6 @@ func createGitHubWithBodyHandler(t *testing.T, expectedURI string, response []by
}

func createGitHubWithPaginationHandler(t *testing.T, _ string, response []byte, _ []byte, expectedStatusCode int, expectedHttpMethod string) http.HandlerFunc {

var repos []github.Repository
err := json.Unmarshal(response, &repos)
assert.NoError(t, err)
Expand Down Expand Up @@ -625,3 +642,34 @@ func createGitHubHandler(t *testing.T, expectedURI string, response []byte, expe
require.NoError(t, err)
}
}

func createGitHubSarifUploadHandler(t *testing.T, _ string, _ []byte, _ int) http.HandlerFunc {
resultSHA := "66d9a06b02a9f3f5fb47bb026a6fa5577647d96e"
return func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "Bearer "+token, r.Header.Get("Authorization"))
if r.RequestURI == "/repos/jfrog/repo-1/commits?page=1&per_page=1&sha=master" {
w.WriteHeader(200)
repositoryCommits := []*github.RepositoryCommit{
{
SHA: &resultSHA,
},
}
jsonRepositoryCommits, err := json.Marshal(repositoryCommits)
require.NoError(t, err)
_, err = w.Write(jsonRepositoryCommits)
require.NoError(t, err)
} else if r.RequestURI == "/repos/jfrog/repo-1/code-scanning/sarifs" {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
bodyAsString := string(body)
if !strings.Contains(bodyAsString, resultSHA) {
assert.Fail(t, "Unexpected Commit SHA")
}
w.WriteHeader(200)
_, err = w.Write([]byte(`{"id" : "b16b0368-01b9-11ed-90a3-cabff0b8ad31", "url": ""}`))
require.NoError(t, err)
} else {
assert.Fail(t, "Unexpected Request URI", r.RequestURI)
}
}
}
4 changes: 4 additions & 0 deletions vcsclient/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ func (client *GitLabClient) UnlabelPullRequest(ctx context.Context, owner, repos
return err
}

func (client *GitLabClient) UploadCodeScanning(ctx context.Context, owner string, repository string, branch string, scanResults string) (string, error) {
return "", errGitLabCodeScanningNotSupported
}

func getProjectID(owner, project string) string {
return fmt.Sprintf("%s/%s", owner, project)
}
Expand Down
7 changes: 7 additions & 0 deletions vcsclient/gitlabcommon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package vcsclient

import (
"errors"
)

var errGitLabCodeScanningNotSupported = errors.New("code scanning is not supported on Gitlab")
7 changes: 7 additions & 0 deletions vcsclient/vcsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ type VcsClient interface {
// name - Label name
// pullRequestID - Pull request ID
UnlabelPullRequest(ctx context.Context, owner, repository, name string, pullRequestID int) error

// UploadCodeScanning Upload Scanning Analysis uploads a scanning analysis file to the relevant git provider
// owner - User or organization
// repository - VCS repository name
// branch - The name of the branch
// scan - Code scanning analysis
UploadCodeScanning(ctx context.Context, owner, repository, branch, scanResults string) (string, error)
}

// CommitInfo contains the details of a commit
Expand Down
3 changes: 1 addition & 2 deletions vcsutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import (
"archive/tar"
"compress/gzip"
"fmt"
"github.com/google/uuid"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"

"github.com/google/uuid"
)

// CreateToken create a random UUID
Expand Down
2 changes: 1 addition & 1 deletion vcsutils/webhookparser/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/http"
"strings"

"github.com/google/go-github/v41/github"
"github.com/google/go-github/v45/github"
"github.com/jfrog/froggit-go/vcsutils"
)

Expand Down

0 comments on commit a1f65b2

Please sign in to comment.