Skip to content

Commit 69347fe

Browse files
feat(whitesourceExecuteStep) Unified audit format of vulnerabilities in SARIF file for whitesource (#4465)
* Unified audit state for whitesource step * reverted unrelated to pr changes * go fmt * Fixed tests and formating * fixed format issue in whitesource/reporting.go --------- Co-authored-by: sumeet patil <[email protected]>
1 parent d01c161 commit 69347fe

6 files changed

+158
-5
lines changed

cmd/whitesourceExecuteScan.go

+24
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type whitesource interface {
4646
GetProjectVulnerabilityReport(projectToken string, format string) ([]byte, error)
4747
GetProjectAlerts(projectToken string) ([]ws.Alert, error)
4848
GetProjectAlertsByType(projectToken, alertType string) ([]ws.Alert, error)
49+
GetProjectIgnoredAlertsByType(projectToken string, alertType string) ([]ws.Alert, error)
4950
GetProjectLibraryLocations(projectToken string) ([]ws.Library, error)
5051
GetProjectHierarchy(projectToken string, includeInHouse bool) ([]ws.Library, error)
5152
}
@@ -511,6 +512,14 @@ func checkPolicyViolations(ctx context.Context, config *ScanOptions, scan *ws.Sc
511512
if err != nil {
512513
return piperutils.Path{}, fmt.Errorf("failed to retrieve project policy alerts from WhiteSource: %w", err)
513514
}
515+
516+
ignoredAlerts, err := sys.GetProjectIgnoredAlertsByType(project.Token, "REJECTED_BY_POLICY_RESOURCE")
517+
if err != nil {
518+
return piperutils.Path{}, fmt.Errorf("failed to retrieve project policy ignored alerts from WhiteSource: %w", err)
519+
}
520+
521+
alerts = append(alerts, ignoredAlerts...)
522+
514523
policyViolationCount += len(alerts)
515524
allAlerts = append(allAlerts, alerts...)
516525
}
@@ -802,6 +811,13 @@ func checkProjectSecurityViolations(config *ScanOptions, cvssSeverityLimit float
802811
return 0, alerts, assessedAlerts, fmt.Errorf("failed to retrieve project alerts from WhiteSource: %w", err)
803812
}
804813

814+
ignoredAlerts, err := sys.GetProjectIgnoredAlertsByType(project.Token, "SECURITY_VULNERABILITY")
815+
if err != nil {
816+
return 0, alerts, assessedAlerts, fmt.Errorf("failed to retrieve project ignored alerts from WhiteSource: %w", err)
817+
}
818+
819+
alerts = append(alerts, ignoredAlerts...)
820+
805821
// filter alerts related to existing assessments
806822
filteredAlerts := []ws.Alert{}
807823
if assessments != nil && len(*assessments) > 0 {
@@ -887,6 +903,14 @@ func aggregateVersionWideVulnerabilities(config *ScanOptions, utils whitesourceU
887903
if err != nil {
888904
return errors.Wrapf(err, "failed to get project alerts by type")
889905
}
906+
907+
ignoredAlerts, err := sys.GetProjectIgnoredAlertsByType(project.Token, "SECURITY_VULNERABILITY")
908+
if err != nil {
909+
return errors.Wrapf(err, "failed to get project ignored alerts by type")
910+
}
911+
912+
alerts = append(alerts, ignoredAlerts...)
913+
890914
log.Entry().Infof("Found project: %s with %v vulnerabilities.", project.Name, len(alerts))
891915
versionWideAlerts = append(versionWideAlerts, alerts...)
892916
}

cmd/whitesourceExecuteScan_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build unit
2-
// +build unit
3-
41
package cmd
52

63
import (

pkg/whitesource/reporting.go

+33
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
211211
partialFingerprints := new(format.PartialFingerprints)
212212
partialFingerprints.PackageURLPlusCVEHash = base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%v+%v", alert.Library.ToPackageUrl().ToString(), alert.Vulnerability.Name)))
213213
result.PartialFingerprints = *partialFingerprints
214+
result.Properties = getAuditInformation(alert)
215+
214216
//append the result
215217
sarif.Runs[0].Results = append(sarif.Runs[0].Results, result)
216218

@@ -268,6 +270,37 @@ func CreateSarifResultFile(scan *Scan, alerts *[]Alert) *format.SARIF {
268270
return &sarif
269271
}
270272

273+
func getAuditInformation(alert Alert) *format.SarifProperties {
274+
unifiedAuditState := "new"
275+
auditMessage := ""
276+
isAudited := false
277+
278+
// unified audit state
279+
switch alert.Status {
280+
case "OPEN":
281+
unifiedAuditState = "new"
282+
case "IGNORE":
283+
unifiedAuditState = "notRelevant"
284+
auditMessage = alert.Comments
285+
}
286+
287+
if alert.Assessment != nil {
288+
unifiedAuditState = string(alert.Assessment.Status)
289+
auditMessage = string(alert.Assessment.Analysis)
290+
}
291+
292+
if unifiedAuditState == string(format.Relevant) ||
293+
unifiedAuditState == string(format.NotRelevant) {
294+
isAudited = true
295+
}
296+
297+
return &format.SarifProperties{
298+
Audited: isAudited,
299+
ToolAuditMessage: auditMessage,
300+
UnifiedAuditState: unifiedAuditState,
301+
}
302+
}
303+
271304
func transformToLevel(cvss2severity, cvss3severity string) string {
272305
cvssseverity := consolidateSeverities(cvss2severity, cvss3severity)
273306
switch cvssseverity {

pkg/whitesource/reporting_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,69 @@ func TestVulnerabilityScore(t *testing.T) {
336336
assert.Equalf(t, test.expected, vulnerabilityScore(test.alert), "run %v failed", i)
337337
}
338338
}
339+
340+
func TestGetAuditInformation(t *testing.T) {
341+
tt := []struct {
342+
name string
343+
alert Alert
344+
expected *format.SarifProperties
345+
}{
346+
{
347+
name: "New not audited alert",
348+
alert: Alert{
349+
Status: "OPEN",
350+
},
351+
expected: &format.SarifProperties{
352+
Audited: false,
353+
ToolAuditMessage: "",
354+
UnifiedAuditState: "new",
355+
},
356+
},
357+
{
358+
name: "Audited alert",
359+
alert: Alert{
360+
Status: "IGNORE",
361+
Comments: "Not relevant alert",
362+
},
363+
expected: &format.SarifProperties{
364+
Audited: true,
365+
ToolAuditMessage: "Not relevant alert",
366+
UnifiedAuditState: "notRelevant",
367+
},
368+
},
369+
{
370+
name: "Alert with incorrect status",
371+
alert: Alert{
372+
Status: "Not correct",
373+
Comments: "Some comment",
374+
},
375+
expected: &format.SarifProperties{
376+
Audited: false,
377+
ToolAuditMessage: "",
378+
UnifiedAuditState: "new",
379+
},
380+
},
381+
{
382+
name: "Audited alert",
383+
alert: Alert{
384+
Assessment: &format.Assessment{
385+
Status: format.NotRelevant,
386+
Analysis: format.FixedByDevTeam,
387+
},
388+
Status: "OPEN",
389+
Comments: "New alert",
390+
},
391+
expected: &format.SarifProperties{
392+
Audited: true,
393+
ToolAuditMessage: string(format.FixedByDevTeam),
394+
UnifiedAuditState: "notRelevant",
395+
},
396+
},
397+
}
398+
399+
for _, test := range tt {
400+
t.Run(test.name, func(t *testing.T) {
401+
assert.Equal(t, getAuditInformation(test.alert), test.expected)
402+
})
403+
}
404+
}

pkg/whitesource/sytemMock.go

+5
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ type SystemMock struct {
1515
Products []Product
1616
Projects []Project
1717
Alerts []Alert
18+
IgnoredAlerts []Alert
1819
AlertType string
1920
AlertError error
2021
Libraries []Library
2122
RiskReport []byte
2223
VulnerabilityReport []byte
2324
}
2425

26+
func (m *SystemMock) GetProjectIgnoredAlertsByType(projectToken string, alertType string) ([]Alert, error) {
27+
return m.IgnoredAlerts, nil
28+
}
29+
2530
// GetProductByName mimics retrieving a Product by name. It returns an error of no Product is stored in the mock.
2631
func (m *SystemMock) GetProductByName(productName string) (Product, error) {
2732
for _, product := range m.Products {

pkg/whitesource/whitesource.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type Alert struct {
5858
CreationDate string `json:"date,omitempty"`
5959
ModifiedDate string `json:"modifiedDate,omitempty"`
6060
Status string `json:"status,omitempty"`
61+
Comments string `json:"comments,omitempty"`
6162
}
6263

6364
// DependencyType returns type of dependency: direct/transitive
@@ -664,6 +665,34 @@ func (s *System) GetProjectAlertsByType(projectToken, alertType string) ([]Alert
664665
return wsResponse.Alerts, nil
665666
}
666667

668+
// GetProjectIgnoredAlertsByType returns all ignored alerts of a certain type for a given project
669+
func (s *System) GetProjectIgnoredAlertsByType(projectToken string, alertType string) ([]Alert, error) {
670+
wsResponse := struct {
671+
Alerts []Alert `json:"alerts"`
672+
}{
673+
Alerts: []Alert{},
674+
}
675+
676+
req := Request{
677+
RequestType: "getProjectIgnoredAlerts",
678+
ProjectToken: projectToken,
679+
}
680+
681+
err := s.sendRequestAndDecodeJSON(req, &wsResponse)
682+
if err != nil {
683+
return nil, err
684+
}
685+
686+
alerts := make([]Alert, 0)
687+
for _, alert := range wsResponse.Alerts {
688+
if alert.Type == alertType {
689+
alerts = append(alerts, alert)
690+
}
691+
}
692+
693+
return alerts, nil
694+
}
695+
667696
// GetProjectLibraryLocations
668697
func (s *System) GetProjectLibraryLocations(projectToken string) ([]Library, error) {
669698
wsResponse := struct {
@@ -721,8 +750,7 @@ func (s *System) sendRequestAndDecodeJSONRecursive(req Request, result interface
721750
return err
722751
}
723752
}
724-
return fmt.Errorf("invalid request, error code %v, message '%s'",
725-
errorResponse.ErrorCode, errorResponse.ErrorMessage)
753+
return fmt.Errorf("invalid request, error code %v, message '%s'", errorResponse.ErrorCode, errorResponse.ErrorMessage)
726754
}
727755

728756
if result != nil {

0 commit comments

Comments
 (0)