-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from liatrio/ENG-1974
Eng 1974
- Loading branch information
Showing
9 changed files
with
500 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,5 @@ | |
# vendor/ | ||
|
||
.vscode/ | ||
|
||
listener/coverage.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,20 @@ | ||
module liatr.io/rode-collector-sonarqube | ||
module github.com/liatrio/rode-collector-sonarqube | ||
|
||
go 1.15 | ||
|
||
require ( | ||
github.com/kr/pretty v0.1.0 // indirect | ||
github.com/liatrio/rode-api v0.0.0-20201111165410-30d476656f6f | ||
github.com/nxadm/tail v1.4.5 // indirect | ||
github.com/onsi/ginkgo v1.14.2 | ||
github.com/onsi/gomega v1.10.3 | ||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | ||
github.com/pkg/errors v0.9.1 // indirect | ||
go.uber.org/zap v1.16.0 | ||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect | ||
golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65 // indirect | ||
golang.org/x/text v0.3.4 // indirect | ||
golang.org/x/tools v0.0.0-20200407143752-a3568bac92ae // indirect | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect | ||
google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 // indirect | ||
google.golang.org/grpc v1.33.2 | ||
google.golang.org/protobuf v1.25.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,138 @@ | ||
package listener | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
) | ||
"time" | ||
|
||
// Event is... | ||
type Event struct { | ||
TaskID string `json:"taskid"` | ||
Status string `json:"status"` | ||
AnalyzedAt string `json:"analyzedat"` | ||
GitCommit string `json:"revision"` | ||
Project *Project `json:"project"` | ||
QualityGate *QualityGate `json:"qualityGate"` | ||
} | ||
"github.com/liatrio/rode-collector-sonarqube/sonar" | ||
"go.uber.org/zap" | ||
|
||
// Project is | ||
type Project struct { | ||
Key string `json:"key"` | ||
Name string `json:"name"` | ||
URL string `json:"url"` | ||
pb "github.com/liatrio/rode-api/proto/v1alpha1" | ||
"github.com/liatrio/rode-api/protodeps/grafeas/proto/v1beta1/common_go_proto" | ||
"github.com/liatrio/rode-api/protodeps/grafeas/proto/v1beta1/grafeas_go_proto" | ||
"github.com/liatrio/rode-api/protodeps/grafeas/proto/v1beta1/package_go_proto" | ||
"github.com/liatrio/rode-api/protodeps/grafeas/proto/v1beta1/vulnerability_go_proto" | ||
"google.golang.org/protobuf/types/known/timestamppb" | ||
) | ||
|
||
type listener struct { | ||
rodeClient pb.RodeClient | ||
logger *zap.Logger | ||
} | ||
|
||
// QualityGate is... | ||
type QualityGate struct { | ||
Conditions []*Condition `json:"conditions"` | ||
Name string `json:"name"` | ||
Status string `json:"status"` | ||
type Listener interface { | ||
ProcessEvent(http.ResponseWriter, *http.Request) | ||
} | ||
|
||
// Condition is... | ||
type Condition struct { | ||
ErrorThreshold string `json:"errorThreshold"` | ||
Metric string `json:"metric"` | ||
OnLeakPeriod bool `json:"onLeakPeriod"` | ||
Operator string `json:"operator"` | ||
Status string `json:"status"` | ||
func NewListener(logger *zap.Logger, client pb.RodeClient) Listener { | ||
return &listener{ | ||
rodeClient: client, | ||
logger: logger, | ||
} | ||
} | ||
|
||
// ProcessEvent handles incoming webhook events | ||
func ProcessEvent(w http.ResponseWriter, request *http.Request) { | ||
log.Print("Received SonarQube event") | ||
func (l *listener) ProcessEvent(w http.ResponseWriter, request *http.Request) { | ||
log := l.logger.Named("ProcessEvent") | ||
|
||
event := &Event{} | ||
event := &sonar.Event{} | ||
if err := json.NewDecoder(request.Body).Decode(event); err != nil { | ||
w.WriteHeader(500) | ||
fmt.Fprintf(w, "Error reading webhook event: %s", err) | ||
fmt.Fprintf(w, "error reading webhook event") | ||
log.Error("error reading webhook event", zap.NamedError("error", err)) | ||
return | ||
} | ||
|
||
log.Printf("SonarQube Event Payload: [%+v]", event) | ||
log.Printf("SonarQube Event Project: [%+v]", event.Project) | ||
log.Printf("SonarQube Event Quality Gate: [%+v]", event.QualityGate) | ||
log.Debug("received sonarqube event", zap.Any("event", event), zap.Any("project", event.Project), zap.Any("qualityGate", event.QualityGate)) | ||
|
||
repo := getRepoFromSonar(event) | ||
|
||
var occurrences []*grafeas_go_proto.Occurrence | ||
for _, condition := range event.QualityGate.Conditions { | ||
log.Printf("SonarQube Event Quality Gate Condition: [%+v]", condition) | ||
log.Debug("sonarqube event quality gate condition", zap.Any("condition", condition)) | ||
occurrence := createQualityGateOccurrence(condition, repo) | ||
occurrences = append(occurrences, occurrence) | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) | ||
defer cancel() | ||
response, err := l.rodeClient.BatchCreateOccurrences(ctx, &pb.BatchCreateOccurrencesRequest{ | ||
Occurrences: occurrences, | ||
}) | ||
if err != nil { | ||
log.Error("error sending occurrences to rode", zap.NamedError("error", err)) | ||
w.WriteHeader(500) | ||
return | ||
} | ||
|
||
log.Debug("response payload", zap.Any("response", response.GetOccurrences())) | ||
w.WriteHeader(200) | ||
} | ||
|
||
func getRepoFromSonar(event *sonar.Event) string { | ||
/* | ||
// Need to add logic to check if they are using developer or enterprise edition, but current API | ||
// only exposes this to admin users. Getting the resource URI is easier in the developer edition and is | ||
// not dependent on a value passed in from the project. It can be done like this: | ||
if isNotCommunityEdition(){ | ||
repoString := fmt.Sprintf("%s:%s",event.Branch.URL,event.GitCommit) | ||
return repoString | ||
} | ||
*/ | ||
|
||
repoString := fmt.Sprintf("%s:%s", event.Properties["sonar.analysis.resourceUriPrefix"], event.GitCommit) | ||
return repoString | ||
} | ||
|
||
func createQualityGateOccurrence(condition *sonar.Condition, repo string) *grafeas_go_proto.Occurrence { | ||
occurrence := &grafeas_go_proto.Occurrence{ | ||
Name: condition.Metric, | ||
Resource: &grafeas_go_proto.Resource{ | ||
Name: repo, | ||
Uri: repo, | ||
}, | ||
NoteName: "projects/notes_project/notes/sonarqube", | ||
Kind: common_go_proto.NoteKind_NOTE_KIND_UNSPECIFIED, | ||
Remediation: "test", | ||
CreateTime: timestamppb.Now(), | ||
// To be changed when a proper occurrence type is determined | ||
Details: &grafeas_go_proto.Occurrence_Vulnerability{ | ||
Vulnerability: &vulnerability_go_proto.Details{ | ||
Type: "test", | ||
Severity: vulnerability_go_proto.Severity_CRITICAL, | ||
ShortDescription: "abc", | ||
LongDescription: "abc123", | ||
RelatedUrls: []*common_go_proto.RelatedUrl{ | ||
{ | ||
Url: "test", | ||
Label: "test", | ||
}, | ||
{ | ||
Url: "test", | ||
Label: "test", | ||
}, | ||
}, | ||
EffectiveSeverity: vulnerability_go_proto.Severity_CRITICAL, | ||
PackageIssue: []*vulnerability_go_proto.PackageIssue{ | ||
{ | ||
SeverityName: "test", | ||
AffectedLocation: &vulnerability_go_proto.VulnerabilityLocation{ | ||
CpeUri: "test", | ||
Package: "test", | ||
Version: &package_go_proto.Version{ | ||
Name: "test", | ||
Revision: "test", | ||
Epoch: 35, | ||
Kind: package_go_proto.Version_MINIMUM, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
return occurrence | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,44 @@ | ||
package listener_test | ||
package listener | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"log" | ||
"testing" | ||
|
||
pb "github.com/liatrio/rode-api/proto/v1alpha1" | ||
"go.uber.org/zap" | ||
"google.golang.org/grpc" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var logger *zap.Logger | ||
|
||
func TestListener(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
log.SetOutput(ioutil.Discard) | ||
RunSpecs(t, "Listener Suite") | ||
} | ||
|
||
var _ = BeforeSuite(func() { | ||
logger, _ = zap.NewDevelopment() | ||
}) | ||
|
||
type mockRodeClient struct { | ||
receivedBatchCreateOccurrenceRequest *pb.BatchCreateOccurrencesRequest | ||
preparedBatchCreateOccurrenceResponse *pb.BatchCreateOccurrencesResponse | ||
expectedError error | ||
} | ||
|
||
func (m *mockRodeClient) BatchCreateOccurrences(ctx context.Context, in *pb.BatchCreateOccurrencesRequest, opts ...grpc.CallOption) (*pb.BatchCreateOccurrencesResponse, error) { | ||
m.receivedBatchCreateOccurrenceRequest = in | ||
|
||
// if we have a prepared response, send it. otherwise, return nil | ||
if m.preparedBatchCreateOccurrenceResponse != nil { | ||
return m.preparedBatchCreateOccurrenceResponse, m.expectedError | ||
} | ||
|
||
return nil, m.expectedError | ||
} |
Oops, something went wrong.