mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
feat(detectExecuteScan) execution of rapid scans (#4211)
Co-authored-by: akram8008 <900658008.akram@email.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> Co-authored-by: Andrei Kireev <andrei.kireev@sap.com> Co-authored-by: ffeldmann <f.feldmann@sap.com> Co-authored-by: sumeet patil <sumeet.patil@sap.com>
This commit is contained in:
parent
cc3bc76943
commit
f4fbf0f1ed
@ -14,20 +14,20 @@ import (
|
||||
"time"
|
||||
|
||||
bd "github.com/SAP/jenkins-library/pkg/blackduck"
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
piperGithub "github.com/SAP/jenkins-library/pkg/github"
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/maven"
|
||||
"github.com/SAP/jenkins-library/pkg/reporting"
|
||||
"github.com/SAP/jenkins-library/pkg/versioning"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/maven"
|
||||
"github.com/SAP/jenkins-library/pkg/orchestrator"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
"github.com/SAP/jenkins-library/pkg/reporting"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
"github.com/SAP/jenkins-library/pkg/toolrecord"
|
||||
"github.com/SAP/jenkins-library/pkg/versioning"
|
||||
|
||||
"github.com/google/go-github/v45/github"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type detectUtils interface {
|
||||
@ -46,14 +46,16 @@ type detectUtils interface {
|
||||
|
||||
GetIssueService() *github.IssuesService
|
||||
GetSearchService() *github.SearchService
|
||||
GetProvider() orchestrator.OrchestratorSpecificConfigProviding
|
||||
}
|
||||
|
||||
type detectUtilsBundle struct {
|
||||
*command.Command
|
||||
*piperutils.Files
|
||||
*piperhttp.Client
|
||||
issues *github.IssuesService
|
||||
search *github.SearchService
|
||||
issues *github.IssuesService
|
||||
search *github.SearchService
|
||||
provider orchestrator.OrchestratorSpecificConfigProviding
|
||||
}
|
||||
|
||||
func (d *detectUtilsBundle) GetIssueService() *github.IssuesService {
|
||||
@ -64,6 +66,10 @@ func (d *detectUtilsBundle) GetSearchService() *github.SearchService {
|
||||
return d.search
|
||||
}
|
||||
|
||||
func (d *detectUtilsBundle) GetProvider() orchestrator.OrchestratorSpecificConfigProviding {
|
||||
return d.provider
|
||||
}
|
||||
|
||||
type blackduckSystem struct {
|
||||
Client bd.Client
|
||||
}
|
||||
@ -104,6 +110,15 @@ func newDetectUtils(client *github.Client) detectUtils {
|
||||
}
|
||||
utils.Stdout(log.Writer())
|
||||
utils.Stderr(log.Writer())
|
||||
|
||||
provider, err := orchestrator.NewOrchestratorSpecificConfigProvider()
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Warning(err)
|
||||
provider = &orchestrator.UnknownOrchestratorConfigProvider{}
|
||||
}
|
||||
|
||||
utils.provider = provider
|
||||
|
||||
return &utils
|
||||
}
|
||||
|
||||
@ -159,8 +174,10 @@ func runDetect(ctx context.Context, config detectExecuteScanOptions, utils detec
|
||||
}
|
||||
}
|
||||
|
||||
blackduckSystem := newBlackduckSystem(config)
|
||||
|
||||
args := []string{"./detect.sh"}
|
||||
args, err = addDetectArgs(args, config, utils)
|
||||
args, err = addDetectArgs(args, config, utils, blackduckSystem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -173,7 +190,6 @@ func runDetect(ctx context.Context, config detectExecuteScanOptions, utils detec
|
||||
utils.SetEnv(envs)
|
||||
|
||||
err = utils.RunShell("/bin/bash", script)
|
||||
blackduckSystem := newBlackduckSystem(config)
|
||||
reportingErr := postScanChecksAndReporting(ctx, config, influx, utils, blackduckSystem)
|
||||
if reportingErr != nil {
|
||||
if strings.Contains(reportingErr.Error(), "License Policy Violations found") {
|
||||
@ -295,7 +311,7 @@ func getDetectScript(config detectExecuteScanOptions, utils detectUtils) error {
|
||||
return utils.DownloadFile("https://detect.synopsys.com/detect7.sh", "detect.sh", nil, nil)
|
||||
}
|
||||
|
||||
func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils) ([]string, error) {
|
||||
func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils, sys *blackduckSystem) ([]string, error) {
|
||||
detectVersionName := getVersionName(config)
|
||||
// Split on spaces, the scanPropeties, so that each property is available as a single string
|
||||
// instead of all properties being part of a single string
|
||||
@ -390,6 +406,18 @@ func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectU
|
||||
args = append(args, fmt.Sprintf("\"--detect.maven.build.command='%v'\"", strings.Join(mavenArgs, " ")))
|
||||
}
|
||||
|
||||
// rapid scan on pull request
|
||||
if utils.GetProvider().IsPullRequest() {
|
||||
log.Entry().Debug("pull request detected")
|
||||
args = append(args, "--detect.blackduck.scan.mode='RAPID'")
|
||||
_, err := sys.Client.GetProjectVersion(config.ProjectName, config.Version)
|
||||
if err == nil {
|
||||
args = append(args, "--detect.blackduck.rapid.compare.mode='BOM_COMPARE_STRICT'")
|
||||
}
|
||||
args = append(args, "--detect.cleanup=false")
|
||||
args = append(args, "--detect.output.path='report'")
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
@ -498,6 +526,33 @@ func isMajorVulnerability(v bd.Vulnerability) bool {
|
||||
}
|
||||
|
||||
func postScanChecksAndReporting(ctx context.Context, config detectExecuteScanOptions, influx *detectExecuteScanInflux, utils detectUtils, sys *blackduckSystem) error {
|
||||
|
||||
if utils.GetProvider().IsPullRequest() {
|
||||
issueNumber, err := strconv.Atoi(utils.GetProvider().GetPullRequestConfig().Key)
|
||||
if err != nil {
|
||||
log.Entry().Warning("Can not get issue number ", err)
|
||||
return nil
|
||||
}
|
||||
commentBody, err := reporting.RapidScanResult("./report")
|
||||
if err != nil {
|
||||
log.Entry().Warning("Couldn't read file of report of rapid scan, error: ", err)
|
||||
return nil
|
||||
}
|
||||
_, _, err = utils.GetIssueService().CreateComment(ctx,
|
||||
config.Owner,
|
||||
config.Repository,
|
||||
issueNumber,
|
||||
&github.IssueComment{
|
||||
Body: &commentBody,
|
||||
})
|
||||
if err != nil {
|
||||
log.Entry().Warning("Can send request to github ", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
errorsOccured := []string{}
|
||||
vulns, err := getVulnerabilitiesWithComponents(config, influx, sys)
|
||||
if err != nil {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
piperGithub "github.com/SAP/jenkins-library/pkg/github"
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
"github.com/SAP/jenkins-library/pkg/orchestrator"
|
||||
|
||||
"github.com/google/go-github/v45/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -25,7 +27,12 @@ type detectTestUtilsBundle struct {
|
||||
downloadedFiles map[string]string // src, dest
|
||||
*mock.ShellMockRunner
|
||||
*mock.FilesMock
|
||||
customEnv []string
|
||||
customEnv []string
|
||||
orchestrator *orchestratorConfigProviderMock
|
||||
}
|
||||
|
||||
func (d *detectTestUtilsBundle) GetProvider() orchestrator.OrchestratorSpecificConfigProviding {
|
||||
return d.orchestrator
|
||||
}
|
||||
|
||||
func (d *detectTestUtilsBundle) GetIssueService() *github.IssuesService {
|
||||
@ -36,6 +43,15 @@ func (d *detectTestUtilsBundle) GetSearchService() *github.SearchService {
|
||||
return nil
|
||||
}
|
||||
|
||||
type orchestratorConfigProviderMock struct {
|
||||
orchestrator.UnknownOrchestratorConfigProvider
|
||||
isPullRequest bool
|
||||
}
|
||||
|
||||
func (o *orchestratorConfigProviderMock) IsPullRequest() bool {
|
||||
return o.isPullRequest
|
||||
}
|
||||
|
||||
type httpMockClient struct {
|
||||
responseBodyForURL map[string]string
|
||||
errorMessageForURL map[string]string
|
||||
@ -73,6 +89,8 @@ func newBlackduckMockSystem(config detectExecuteScanOptions) blackduckSystem {
|
||||
"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/vunlerable-bom-components?limit=999&offset=0": vulnerabilitiesContent,
|
||||
"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/components?filter=policyCategory%3Alicense&limit=999&offset=0": componentsContent,
|
||||
"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/policy-status": policyStatusContent,
|
||||
"https://my.blackduck.system/api/projects?q=name%3ARapid_scan_on_PRs": projectContentRapidScan,
|
||||
"https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions?limit=100&offset=0": projectVersionContentRapid,
|
||||
},
|
||||
header: map[string]http.Header{},
|
||||
}
|
||||
@ -193,6 +211,48 @@ const (
|
||||
"severityLevels": [{"name":"BLOCKER", "value": 1}, {"name": "CRITICAL", "value": 1}]
|
||||
}
|
||||
}`
|
||||
projectContentRapidScan = `{
|
||||
"totalCount": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Rapid_scan_on_PRs",
|
||||
"_meta": {
|
||||
"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf",
|
||||
"links": [
|
||||
{
|
||||
"rel": "versions",
|
||||
"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
projectVersionContentRapid = `{
|
||||
"totalCount": 1,
|
||||
"items": [
|
||||
{
|
||||
"versionName": "1.0",
|
||||
"_meta": {
|
||||
"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions/54357fds-0ee6-414f-9054-90d549c69c36",
|
||||
"links": [
|
||||
{
|
||||
"rel": "components",
|
||||
"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/components"
|
||||
},
|
||||
{
|
||||
"rel": "vulnerable-components",
|
||||
"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/vunlerable-bom-components"
|
||||
},
|
||||
{
|
||||
"rel": "policy-status",
|
||||
"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/policy-status"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
)
|
||||
|
||||
func (c *detectTestUtilsBundle) RunExecutable(string, ...string) error {
|
||||
@ -222,10 +282,11 @@ func (w *detectTestUtilsBundle) CreateIssue(ghCreateIssueOptions *piperGithub.Cr
|
||||
return nil
|
||||
}
|
||||
|
||||
func newDetectTestUtilsBundle() *detectTestUtilsBundle {
|
||||
func newDetectTestUtilsBundle(isPullRequest bool) *detectTestUtilsBundle {
|
||||
utilsBundle := detectTestUtilsBundle{
|
||||
ShellMockRunner: &mock.ShellMockRunner{},
|
||||
FilesMock: &mock.FilesMock{},
|
||||
orchestrator: &orchestratorConfigProviderMock{isPullRequest: isPullRequest},
|
||||
}
|
||||
return &utilsBundle
|
||||
}
|
||||
@ -235,7 +296,7 @@ func TestRunDetect(t *testing.T) {
|
||||
t.Run("success case", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
utilsMock := newDetectTestUtilsBundle()
|
||||
utilsMock := newDetectTestUtilsBundle(false)
|
||||
utilsMock.AddFile("detect.sh", []byte(""))
|
||||
err := runDetect(ctx, detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{})
|
||||
|
||||
@ -251,7 +312,7 @@ func TestRunDetect(t *testing.T) {
|
||||
t.Run("success case detect 6", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
utilsMock := newDetectTestUtilsBundle()
|
||||
utilsMock := newDetectTestUtilsBundle(false)
|
||||
utilsMock.AddFile("detect.sh", []byte(""))
|
||||
options := detectExecuteScanOptions{
|
||||
CustomEnvironmentVariables: []string{"DETECT_LATEST_RELEASE_VERSION=6.8.0"},
|
||||
@ -270,7 +331,7 @@ func TestRunDetect(t *testing.T) {
|
||||
t.Run("success case detect 6 from OS env", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
utilsMock := newDetectTestUtilsBundle()
|
||||
utilsMock := newDetectTestUtilsBundle(false)
|
||||
utilsMock.AddFile("detect.sh", []byte(""))
|
||||
utilsMock.customEnv = []string{"DETECT_LATEST_RELEASE_VERSION=6.8.0"}
|
||||
err := runDetect(ctx, detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{})
|
||||
@ -287,7 +348,7 @@ func TestRunDetect(t *testing.T) {
|
||||
t.Run("failure case", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
utilsMock := newDetectTestUtilsBundle()
|
||||
utilsMock := newDetectTestUtilsBundle(false)
|
||||
utilsMock.ShouldFailOnCommand = map[string]error{"./detect.sh --blackduck.url= --blackduck.api.token= \"--detect.project.name=''\" \"--detect.project.version.name=''\" \"--detect.code.location.name=''\" --detect.source.path='.'": fmt.Errorf("")}
|
||||
utilsMock.ExitCode = 3
|
||||
utilsMock.AddFile("detect.sh", []byte(""))
|
||||
@ -300,7 +361,7 @@ func TestRunDetect(t *testing.T) {
|
||||
t.Run("maven parameters", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
utilsMock := newDetectTestUtilsBundle()
|
||||
utilsMock := newDetectTestUtilsBundle(false)
|
||||
utilsMock.CurrentDir = "root_folder"
|
||||
utilsMock.AddFile("detect.sh", []byte(""))
|
||||
err := runDetect(ctx, detectExecuteScanOptions{
|
||||
@ -322,9 +383,10 @@ func TestRunDetect(t *testing.T) {
|
||||
func TestAddDetectArgs(t *testing.T) {
|
||||
t.Parallel()
|
||||
testData := []struct {
|
||||
args []string
|
||||
options detectExecuteScanOptions
|
||||
expected []string
|
||||
args []string
|
||||
options detectExecuteScanOptions
|
||||
isPullRequest bool
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
args: []string{"--testProp1=1"},
|
||||
@ -645,13 +707,76 @@ func TestAddDetectArgs(t *testing.T) {
|
||||
"--detect.source.path='.'",
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"--testProp1=1"},
|
||||
options: detectExecuteScanOptions{
|
||||
ServerURL: "https://server.url",
|
||||
Token: "apiToken",
|
||||
ProjectName: "Rapid_scan_on_PRs",
|
||||
Version: "1.0",
|
||||
VersioningModel: "major-minor",
|
||||
CodeLocation: "",
|
||||
ScanPaths: []string{"path1", "path2"},
|
||||
MinScanInterval: 4,
|
||||
CustomScanVersion: "1.0",
|
||||
},
|
||||
isPullRequest: true,
|
||||
expected: []string{
|
||||
"--testProp1=1",
|
||||
"--detect.blackduck.signature.scanner.arguments='--min-scan-interval=4'",
|
||||
"--blackduck.url=https://server.url",
|
||||
"--blackduck.api.token=apiToken",
|
||||
"\"--detect.project.name='Rapid_scan_on_PRs'\"",
|
||||
"\"--detect.project.version.name='1.0'\"",
|
||||
"\"--detect.code.location.name='Rapid_scan_on_PRs/1.0'\"",
|
||||
"--detect.blackduck.signature.scanner.paths=path1,path2",
|
||||
"--detect.source.path='.'",
|
||||
"--detect.blackduck.scan.mode='RAPID'",
|
||||
"--detect.blackduck.rapid.compare.mode='BOM_COMPARE_STRICT'",
|
||||
"--detect.cleanup=false",
|
||||
"--detect.output.path='report'",
|
||||
},
|
||||
},
|
||||
{
|
||||
args: []string{"--testProp1=1"},
|
||||
options: detectExecuteScanOptions{
|
||||
ServerURL: "https://server.url",
|
||||
Token: "apiToken",
|
||||
ProjectName: "Rapid_scan_on_PRs",
|
||||
Version: "2.0",
|
||||
VersioningModel: "major-minor",
|
||||
CodeLocation: "",
|
||||
ScanPaths: []string{"path1", "path2"},
|
||||
MinScanInterval: 4,
|
||||
CustomScanVersion: "2.0",
|
||||
},
|
||||
isPullRequest: true,
|
||||
expected: []string{
|
||||
"--testProp1=1",
|
||||
"--detect.blackduck.signature.scanner.arguments='--min-scan-interval=4'",
|
||||
"--blackduck.url=https://server.url",
|
||||
"--blackduck.api.token=apiToken",
|
||||
"\"--detect.project.name='Rapid_scan_on_PRs'\"",
|
||||
"\"--detect.project.version.name='2.0'\"",
|
||||
"\"--detect.code.location.name='Rapid_scan_on_PRs/2.0'\"",
|
||||
"--detect.blackduck.signature.scanner.paths=path1,path2",
|
||||
"--detect.source.path='.'",
|
||||
"--detect.blackduck.scan.mode='RAPID'",
|
||||
"--detect.cleanup=false",
|
||||
"--detect.output.path='report'",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testData {
|
||||
v := v
|
||||
t.Run(fmt.Sprintf("run %v", k), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got, err := addDetectArgs(v.args, v.options, newDetectTestUtilsBundle())
|
||||
|
||||
config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: v.options.ProjectName, Version: v.options.Version, CustomScanVersion: v.options.CustomScanVersion}
|
||||
sys := newBlackduckMockSystem(config)
|
||||
|
||||
got, err := addDetectArgs(v.args, v.options, newDetectTestUtilsBundle(v.isPullRequest), &sys)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, v.expected, got)
|
||||
})
|
||||
@ -681,7 +806,7 @@ func TestPostScanChecksAndReporting(t *testing.T) {
|
||||
t.Run("Reporting after scan", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: "SHC-PiperTest", Version: "", CustomScanVersion: "1.0"}
|
||||
utils := newDetectTestUtilsBundle()
|
||||
utils := newDetectTestUtilsBundle(false)
|
||||
sys := newBlackduckMockSystem(config)
|
||||
err := postScanChecksAndReporting(ctx, config, &detectExecuteScanInflux{}, utils, &sys)
|
||||
|
||||
|
@ -51,7 +51,7 @@ If you have configured your orchestrator to detect pull requests, then the `dete
|
||||
```
|
||||
|
||||
2. Enable detecExecuationScan in the orchestrator.
|
||||
For example:
|
||||
For example:
|
||||
|
||||
```
|
||||
@Library('piper-lib') _
|
||||
|
420
pkg/reporting/pullRequestReport.go
Normal file
420
pkg/reporting/pullRequestReport.go
Normal file
@ -0,0 +1,420 @@
|
||||
package reporting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
)
|
||||
|
||||
// Components - for parsing from file
|
||||
type Components []Component
|
||||
|
||||
type Component struct {
|
||||
ComponentName string `json:"componentName"`
|
||||
ComponentVersion string `json:"versionName"`
|
||||
ComponentIdentifier string `json:"componentIdentifier"`
|
||||
ViolatingPolicyNames []string `json:"violatingPolicyNames"`
|
||||
PolicyViolationVulnerabilities []PolicyViolationVulnerability `json:"policyViolationVulnerabilities"`
|
||||
PolicyViolationLicenses []PolicyViolationLicense `json:"policyViolationLicenses"`
|
||||
WarningMessage string `json:"warningMessage"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
}
|
||||
|
||||
type PolicyViolationVulnerability struct {
|
||||
Name string `json:"name"`
|
||||
ViolatingPolicyNames []string `json:"ViolatingPolicyNames"`
|
||||
WarningMessage string `json:"warningMessage"`
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
Meta Meta `json:"_meta"`
|
||||
}
|
||||
|
||||
type PolicyViolationLicense struct {
|
||||
LicenseName string `json:"licenseName"`
|
||||
ViolatingPolicyNames []string `json:"violatingPolicyNames"`
|
||||
Meta Meta `json:"_meta"`
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
Href string `json:"href"`
|
||||
}
|
||||
|
||||
// RapidScanReport - for commenting to pull requests
|
||||
type RapidScanReport struct {
|
||||
Success bool
|
||||
|
||||
ExecutedTime string
|
||||
|
||||
MainTableHeaders []string
|
||||
MainTableValues [][]string
|
||||
|
||||
VulnerabilitiesTable []Vulnerabilities
|
||||
LicensesTable []Licenses
|
||||
OtherViolationsTable []OtherViolations
|
||||
}
|
||||
|
||||
type Vulnerabilities struct {
|
||||
PolicyViolationName string
|
||||
Values []Vulnerability
|
||||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
VulnerabilityID string
|
||||
VulnerabilityScore string
|
||||
ComponentName string
|
||||
VulnerabilityHref string
|
||||
}
|
||||
|
||||
type Licenses struct {
|
||||
PolicyViolationName string
|
||||
Values []License
|
||||
}
|
||||
|
||||
type License struct {
|
||||
LicenseName string
|
||||
ComponentName string
|
||||
LicenseHref string
|
||||
}
|
||||
|
||||
type OtherViolations struct {
|
||||
PolicyViolationName string
|
||||
Values []OtherViolation
|
||||
}
|
||||
|
||||
type OtherViolation struct {
|
||||
ComponentName string
|
||||
}
|
||||
|
||||
const rapidReportMdTemplate = `
|
||||
## {{if .Success}}:heavy_check_mark: OSS related checks passed successfully
|
||||
### :clipboard: OSS related checks executed by Black Duck - rapid scan passed successfully.
|
||||
<a href="https://community.synopsys.com/s/document-item?bundleId=integrations-detect&topicId=downloadingandrunning%2Frapidscan.html&_LANG=enus"><h3>RAPID SCAN</h3> </a>
|
||||
|
||||
{{else}} :x: OSS related checks failed
|
||||
### :clipboard: Policies violated by added OSS components
|
||||
<table>
|
||||
<tr>{{range $s := .MainTableHeaders -}}<td><b>{{$s}}</b></td>{{- end}}</tr>
|
||||
{{range $s := .MainTableValues -}}<tr>{{range $s1 := $s }}<td>{{$s1}}</td>{{- end}}</tr>
|
||||
{{- end}}
|
||||
</table>
|
||||
|
||||
{{range $index := .VulnerabilitiesTable -}}
|
||||
<details><summary>
|
||||
{{$len := len $index.Values}}
|
||||
{{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3>
|
||||
{{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}}
|
||||
</summary>
|
||||
<table>
|
||||
<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability Score</b></td><td><b>Component Name</b></td></tr>
|
||||
{{range $value := $index.Values -}}
|
||||
<tr>
|
||||
<td> <a href="{{$value.VulnerabilityHref}}"> {{$value.VulnerabilityID}} </a> </td><td>{{$value.VulnerabilityScore}}</td><td>{{$value.ComponentName}}</td>
|
||||
</tr>
|
||||
{{end -}}
|
||||
</table>
|
||||
</details>
|
||||
{{end -}}
|
||||
{{range $index := .LicensesTable -}}
|
||||
<details><summary>
|
||||
{{$len := len $index.Values}}
|
||||
{{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3>
|
||||
{{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}}
|
||||
</summary>
|
||||
<table>
|
||||
<tr><td><b>License Name</b></td><td><b>Component Name</b></td></tr>
|
||||
{{range $value := $index.Values -}}
|
||||
<tr><td> <a href="{{$value.LicenseHref}}"> {{$value.LicenseName}} </a> </td><td>{{$value.ComponentName}}</td></tr>
|
||||
{{end -}}
|
||||
</table>
|
||||
</details>
|
||||
{{end -}}
|
||||
{{range $index := .OtherViolationsTable -}}
|
||||
<details><summary>
|
||||
{{$len := len $index.Values}}
|
||||
{{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3>
|
||||
{{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}}
|
||||
</summary>
|
||||
<table>
|
||||
<tr><td><b>Component Name</b></td></tr>
|
||||
{{range $value := $index.Values -}}
|
||||
<tr><td>{{$value.ComponentName}}</td></tr>
|
||||
{{end -}}
|
||||
</table>
|
||||
</details>
|
||||
{{end -}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
// RapidScanResult reads result of Rapid scan from generated file
|
||||
func RapidScanResult(dir string) (string, error) {
|
||||
components, removeDir, err := findAndReadJsonFile(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if components == nil {
|
||||
return "", errors.New("couldn't parse info from file")
|
||||
}
|
||||
|
||||
buf, err := createMarkdownReport(components)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = os.RemoveAll(removeDir)
|
||||
if err != nil {
|
||||
log.Entry().Error("Couldn't remove report file", err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
type Files []os.DirEntry
|
||||
|
||||
// findLastCreatedDir finds last created directory
|
||||
func findLastCreatedDir(directories []os.DirEntry) os.DirEntry {
|
||||
lastCreatedDir := directories[0]
|
||||
for _, dir := range directories {
|
||||
if dir.Name() > lastCreatedDir.Name() {
|
||||
lastCreatedDir = dir
|
||||
}
|
||||
}
|
||||
return lastCreatedDir
|
||||
}
|
||||
|
||||
// findAndReadJsonFile find file BlackDuck_DeveloperMode_Result.json generated by detectExecuteStep and read it
|
||||
func findAndReadJsonFile(dir string) (*Components, string, error) {
|
||||
var err error
|
||||
filePath := dir + "/runs"
|
||||
allFiles, err := os.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if allFiles == nil {
|
||||
return nil, "", errors.New("no report files")
|
||||
}
|
||||
lastDir := findLastCreatedDir(allFiles)
|
||||
removeDir := filePath + "/" + lastDir.Name()
|
||||
filePath = filePath + "/" + lastDir.Name() + "/scan"
|
||||
files, err := os.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if files == nil {
|
||||
return nil, "", errors.New("no report files")
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasSuffix(file.Name(), "BlackDuck_DeveloperMode_Result.json") {
|
||||
var result Components
|
||||
jsonFile, err := os.Open(filePath + "/" + file.Name())
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
fileBody, err := io.ReadAll(jsonFile)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
err = json.Unmarshal(fileBody, &result)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
err = jsonFile.Close()
|
||||
if err != nil {
|
||||
log.Entry().Error(fmt.Sprintf("Couldn't close %s", jsonFile.Name()), err)
|
||||
}
|
||||
return &result, removeDir, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
// createMarkdownReport creates markdown report to upload it as GitHub PR comment
|
||||
func createMarkdownReport(components *Components) (*bytes.Buffer, error) {
|
||||
// preparing report
|
||||
var scanReport RapidScanReport
|
||||
scanReport.Success = true
|
||||
|
||||
// getting reports to maps
|
||||
allPolicyViolationsMapUsed := make(map[string]bool)
|
||||
countPolicyViolationComponent := make(map[string]map[string]int)
|
||||
vulnerabilities := make(map[string][]Vulnerability)
|
||||
licenses := make(map[string][]License)
|
||||
otherViolations := make(map[string][]OtherViolation)
|
||||
componentNames := make([]string, len(*components))
|
||||
|
||||
for idx, component := range *components {
|
||||
componentName := component.ComponentName + " " + component.ComponentVersion + " (" + component.ComponentIdentifier + ")"
|
||||
componentNames[idx] = componentName
|
||||
|
||||
// for others
|
||||
for _, policyViolationName := range component.ViolatingPolicyNames {
|
||||
if !allPolicyViolationsMapUsed[policyViolationName] {
|
||||
allPolicyViolationsMapUsed[policyViolationName] = true
|
||||
scanReport.MainTableHeaders = append(scanReport.MainTableHeaders, policyViolationName)
|
||||
}
|
||||
if countPolicyViolationComponent[policyViolationName] == nil {
|
||||
countPolicyViolationComponent[policyViolationName] = make(map[string]int)
|
||||
}
|
||||
msg := component.ErrorMessage + " " + component.WarningMessage
|
||||
if strings.Contains(msg, policyViolationName) {
|
||||
countPolicyViolationComponent[policyViolationName][componentName]++
|
||||
otherViolations[policyViolationName] = append(otherViolations[policyViolationName], OtherViolation{ComponentName: componentName})
|
||||
}
|
||||
}
|
||||
|
||||
// for Vulnerabilities
|
||||
for _, policyVulnerability := range component.PolicyViolationVulnerabilities {
|
||||
for _, policyViolationName := range policyVulnerability.ViolatingPolicyNames {
|
||||
if countPolicyViolationComponent[policyViolationName] == nil {
|
||||
countPolicyViolationComponent[policyViolationName] = make(map[string]int)
|
||||
}
|
||||
countPolicyViolationComponent[policyViolationName][componentName]++
|
||||
vulnerabilities[policyViolationName] = append(vulnerabilities[policyViolationName],
|
||||
Vulnerability{
|
||||
VulnerabilityID: policyVulnerability.Name,
|
||||
VulnerabilityHref: policyVulnerability.Meta.Href,
|
||||
VulnerabilityScore: getScore(policyVulnerability.ErrorMessage, "score") + " " + getScore(policyVulnerability.ErrorMessage, "severity"),
|
||||
ComponentName: componentName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// for Licenses
|
||||
for _, policyViolationLicense := range component.PolicyViolationLicenses {
|
||||
for _, policyViolationName := range policyViolationLicense.ViolatingPolicyNames {
|
||||
if countPolicyViolationComponent[policyViolationName] == nil {
|
||||
countPolicyViolationComponent[policyViolationName] = make(map[string]int)
|
||||
}
|
||||
countPolicyViolationComponent[policyViolationName][componentName]++
|
||||
licenses[policyViolationName] = append(licenses[policyViolationName],
|
||||
License{
|
||||
LicenseName: policyViolationLicense.LicenseName,
|
||||
LicenseHref: policyViolationLicense.Meta.Href + "/license-terms",
|
||||
ComponentName: componentName,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scanReport.MainTableHeaders != nil && componentNames != nil {
|
||||
scanReport.Success = false
|
||||
|
||||
// MainTable sort & copy
|
||||
sort.Strings(scanReport.MainTableHeaders)
|
||||
sort.Strings(componentNames)
|
||||
scanReport.MainTableHeaders = append([]string{"Component name"}, scanReport.MainTableHeaders...)
|
||||
for i := range componentNames {
|
||||
scanReport.MainTableValues = append(scanReport.MainTableValues, []string{})
|
||||
scanReport.MainTableValues[i] = append(scanReport.MainTableValues[i], componentNames[i])
|
||||
for j := 1; j < len(scanReport.MainTableHeaders); j++ {
|
||||
policyV := scanReport.MainTableHeaders[j]
|
||||
comp := componentNames[i]
|
||||
count := strconv.Itoa(countPolicyViolationComponent[policyV][comp])
|
||||
scanReport.MainTableValues[i] = append(scanReport.MainTableValues[i], count)
|
||||
}
|
||||
}
|
||||
|
||||
// VulnerabilitiesTable sort & copy
|
||||
for key := range vulnerabilities {
|
||||
item := vulnerabilities[key]
|
||||
sort.Slice(item, func(i, j int) bool {
|
||||
return scoreLogicSort(item[i].VulnerabilityScore, item[j].VulnerabilityScore)
|
||||
})
|
||||
scanReport.VulnerabilitiesTable = append(scanReport.VulnerabilitiesTable, Vulnerabilities{
|
||||
PolicyViolationName: key,
|
||||
Values: item,
|
||||
})
|
||||
}
|
||||
sort.Slice(scanReport.VulnerabilitiesTable, func(i, j int) bool {
|
||||
return scanReport.VulnerabilitiesTable[i].PolicyViolationName < scanReport.VulnerabilitiesTable[j].PolicyViolationName
|
||||
})
|
||||
|
||||
// LicensesTable sort & copy
|
||||
for key := range licenses {
|
||||
item := licenses[key]
|
||||
sort.Slice(item, func(i, j int) bool {
|
||||
if item[i].LicenseName < item[j].LicenseName {
|
||||
return true
|
||||
}
|
||||
if item[i].LicenseName > item[j].LicenseName {
|
||||
return false
|
||||
}
|
||||
return item[i].ComponentName < item[j].ComponentName
|
||||
})
|
||||
scanReport.LicensesTable = append(scanReport.LicensesTable, Licenses{
|
||||
PolicyViolationName: key,
|
||||
Values: item,
|
||||
})
|
||||
}
|
||||
sort.Slice(scanReport.LicensesTable, func(i, j int) bool {
|
||||
return scanReport.LicensesTable[i].PolicyViolationName < scanReport.LicensesTable[j].PolicyViolationName
|
||||
})
|
||||
|
||||
// OtherViolationsTable sort & copy
|
||||
for key := range otherViolations {
|
||||
item := otherViolations[key]
|
||||
sort.Slice(item, func(i, j int) bool {
|
||||
return item[i].ComponentName < item[j].ComponentName
|
||||
})
|
||||
scanReport.OtherViolationsTable = append(scanReport.OtherViolationsTable, OtherViolations{
|
||||
PolicyViolationName: key,
|
||||
Values: item,
|
||||
})
|
||||
}
|
||||
sort.Slice(scanReport.OtherViolationsTable, func(i, j int) bool {
|
||||
return scanReport.OtherViolationsTable[i].PolicyViolationName < scanReport.OtherViolationsTable[j].PolicyViolationName
|
||||
})
|
||||
}
|
||||
|
||||
tmpl, err := template.New("report").Parse(rapidReportMdTemplate)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create Markdown report template err:" + err.Error())
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = tmpl.Execute(buf, scanReport)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to create Markdown report template err:" + err.Error())
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// getScore extracts score or severity from error message
|
||||
func getScore(message, key string) string {
|
||||
indx := strings.Index(message, key)
|
||||
if indx == -1 {
|
||||
return ""
|
||||
}
|
||||
var result string
|
||||
var notFirstSpace bool
|
||||
for _, s := range message[indx+len(key):] {
|
||||
if s == ' ' && notFirstSpace {
|
||||
break
|
||||
}
|
||||
notFirstSpace = true
|
||||
result = result + string(s)
|
||||
}
|
||||
return strings.Trim(result, " ")
|
||||
}
|
||||
|
||||
// scoreLogicSort sorts two scores
|
||||
func scoreLogicSort(iStr, jStr string) bool {
|
||||
if strings.Contains(iStr, "10.0") {
|
||||
return true
|
||||
} else if strings.Contains(jStr, "10.0") {
|
||||
return false
|
||||
}
|
||||
if iStr >= jStr {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
249
pkg/reporting/pullRequestReport_test.go
Normal file
249
pkg/reporting/pullRequestReport_test.go
Normal file
@ -0,0 +1,249 @@
|
||||
package reporting
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Testing createMarkdownReport function
|
||||
func TestCreateMarkdownReport(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
components *Components
|
||||
expectedErr error
|
||||
expectedReport string
|
||||
}{
|
||||
|
||||
{
|
||||
testName: "Vulnerabilities were found",
|
||||
components: &Components{
|
||||
{
|
||||
ComponentName: "qs - QS Querystring",
|
||||
ComponentVersion: "5.2.1",
|
||||
ComponentIdentifier: "npmjs:qs/5.2.1",
|
||||
ViolatingPolicyNames: []string{
|
||||
"High Vulnerability Security Issue",
|
||||
},
|
||||
PolicyViolationVulnerabilities: []PolicyViolationVulnerability{
|
||||
{
|
||||
Name: "CVE-2017-1000048",
|
||||
ViolatingPolicyNames: []string{"High Vulnerability Security Issue"},
|
||||
WarningMessage: "",
|
||||
ErrorMessage: "Component qs - QS Querystring version 5.2.1 with ID npmjs:qs/5.2.1 violates policy" +
|
||||
" High Vulnerability Security Issue: found vulnerability CVE-2017-1000048 with severity HIGH and CVSS score 7.5",
|
||||
Meta: Meta{
|
||||
Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2017-1000048",
|
||||
},
|
||||
},
|
||||
},
|
||||
PolicyViolationLicenses: nil,
|
||||
WarningMessage: "",
|
||||
ErrorMessage: "",
|
||||
},
|
||||
{
|
||||
ComponentName: "Lodash",
|
||||
ComponentVersion: "4.17.10",
|
||||
ComponentIdentifier: "npmjs:lodash/4.17.10",
|
||||
ViolatingPolicyNames: []string{
|
||||
"High Vulnerability Security Issue",
|
||||
"Test High Severity Vuln Filter",
|
||||
"OutdatedFOSSLibraries",
|
||||
},
|
||||
PolicyViolationVulnerabilities: []PolicyViolationVulnerability{
|
||||
{
|
||||
Name: "CVE-2019-10744",
|
||||
ViolatingPolicyNames: []string{
|
||||
"High Vulnerability Security Issue",
|
||||
"Test High Severity Vuln Filter",
|
||||
},
|
||||
WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln " +
|
||||
"Filter: found vulnerability CVE-2019-10744 with severity CRITICAL and CVSS score 9.1",
|
||||
ErrorMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy High Vulnerability " +
|
||||
"Security Issue: found vulnerability CVE-2019-10744 with severity CRITICAL and CVSS score 9.1",
|
||||
Meta: Meta{
|
||||
Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744"},
|
||||
},
|
||||
{
|
||||
Name: "CVE-2020-8203",
|
||||
ViolatingPolicyNames: []string{
|
||||
"High Vulnerability Security Issue",
|
||||
"Test High Severity Vuln Filter",
|
||||
},
|
||||
WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test " +
|
||||
"High Severity Vuln Filter: found vulnerability CVE-2020-8203 with severity HIGH and CVSS score 7.4",
|
||||
ErrorMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln Filter: " +
|
||||
"found vulnerability CVE-2020-8203 with severity HIGH and CVSS score 7.4",
|
||||
Meta: Meta{
|
||||
Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BDSA-2019-3842",
|
||||
ViolatingPolicyNames: []string{
|
||||
"High Vulnerability Security Issue",
|
||||
"Test High Severity Vuln Filter",
|
||||
},
|
||||
WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln Filter: found vulnerability BDSA-2019-3842 with severity HIGH and CVSS score 7.1",
|
||||
ErrorMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy High Vulnerability Security Issue: found vulnerability BDSA-2019-3842 with severity HIGH and CVSS score 7.1",
|
||||
Meta: Meta{
|
||||
Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842",
|
||||
},
|
||||
},
|
||||
},
|
||||
PolicyViolationLicenses: nil,
|
||||
WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy OutdatedFOSSLibraries",
|
||||
ErrorMessage: "",
|
||||
},
|
||||
{
|
||||
ComponentName: "Chalk",
|
||||
ComponentVersion: "1.1.3",
|
||||
ComponentIdentifier: "npmjs:chalk/1.1.3",
|
||||
ViolatingPolicyNames: []string{
|
||||
"OutdatedFOSSLibraries",
|
||||
},
|
||||
PolicyViolationVulnerabilities: nil,
|
||||
PolicyViolationLicenses: nil,
|
||||
WarningMessage: "Component Chalk version 1.1.3 with ID npmjs:chalk/1.1.3 violates policy OutdatedFOSSLibraries",
|
||||
ErrorMessage: "",
|
||||
},
|
||||
},
|
||||
expectedReport: "\n## :x: OSS related checks failed\n ### :clipboard: Policies violated by added OSS components\n " +
|
||||
"<table>\n <tr><td><b>Component name</b></td><td><b>High Vulnerability Security Issue</b></td><td><b>OutdatedFOSSLibraries</b></td><td><b>" +
|
||||
"Test High Severity Vuln Filter</b></td></tr>\n <tr><td>Chalk 1.1.3 (npmjs:chalk/1.1.3)</td><td>0</td><td>1</td><td>0</td></tr><tr><td>Lodash " +
|
||||
"4.17.10 (npmjs:lodash/4.17.10)</td><td>3</td><td>1</td><td>3</td></tr><tr><td>qs - QS Querystring 5.2.1 " +
|
||||
"(npmjs:qs/5.2.1)</td><td>1</td><td>0</td><td>0</td></tr>\n </table>\n\n<details><summary>\n\n<h3> 4 Policy " +
|
||||
"Violations of High Vulnerability Security Issue </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability" +
|
||||
" Score</b></td><td><b>Component Name</b></td></tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744\"> CVE-2019-10744 </a> </td><td>9.1 CRITICAL</td><td>Lodash 4.17.10 " +
|
||||
"(npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2017-1000048\"> " +
|
||||
"CVE-2017-1000048 </a> </td><td>7.5 HIGH</td><td>qs - QS Querystring 5.2.1 (npmjs:qs/5.2.1)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> " +
|
||||
"<a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203\"> CVE-2020-8203 </a> </td><td>7.4 HIGH</td><td>Lodash " +
|
||||
"4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842\"> " +
|
||||
"BDSA-2019-3842 </a> </td><td>7.1 HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t</table>\n</details>\n<details><summary>\n\n<h3> " +
|
||||
"3 Policy Violations of Test High Severity Vuln Filter </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability " +
|
||||
"Score</b></td><td><b>Component Name</b></td></tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744\"> " +
|
||||
"CVE-2019-10744 </a> </td><td>9.1 CRITICAL</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> " +
|
||||
"<a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203\"> CVE-2020-8203 </a> </td><td>7.4 " +
|
||||
"HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842\"> " +
|
||||
"BDSA-2019-3842 </a> </td><td>7.1 HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t</table>\n</details>\n<details><summary>\n\n<h3> " +
|
||||
"2 Policy Violations of OutdatedFOSSLibraries </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Component Name</b></td></tr>\n\t\t<tr><td>Chalk 1.1.3 " +
|
||||
"(npmjs:chalk/1.1.3)</td></tr>\n\t\t<tr><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td></tr>\n\t\t</table>\n</details>\n\n",
|
||||
},
|
||||
{
|
||||
testName: "No vulnerabilities && successful build",
|
||||
components: &Components{},
|
||||
expectedReport: "\n## :heavy_check_mark: OSS related checks passed successfully\n ### :clipboard: OSS related checks executed by Black Duck " +
|
||||
"- rapid scan passed successfully.\n" +
|
||||
" <a href=\"https://community.synopsys.com/s/document-item?bundleId=integrations-detect&topicId=downloadingandrunning%2Frapidscan.html&_LANG=enus\">" +
|
||||
"<h3>RAPID SCAN</h3> </a>\n\n\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testCases {
|
||||
t.Run(c.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
buf, err := createMarkdownReport(c.components)
|
||||
|
||||
assert.Equal(t, c.expectedErr, err)
|
||||
assert.Equal(t, c.expectedReport, buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Testing getScore function
|
||||
func TestGetScore(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
message string
|
||||
key string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
testName: "Score 7.5",
|
||||
message: "Component qs - QS Querystring version 5.2.1 with ID npmjs:qs/5.2.1 violates policy High " +
|
||||
"Vulnerability Security Issue: found vulnerability CVE-2017-1000048 with severity HIGH and CVSS score 7.5",
|
||||
key: "score",
|
||||
expected: "7.5",
|
||||
},
|
||||
{
|
||||
testName: "CRITICAL severity",
|
||||
message: "Component minimist version 0.0.8 with ID npmjs:minimist/0.0.8 violates policy High " +
|
||||
"Vulnerability Security Issue: found vulnerability CVE-2021-44906 with severity CRITICAL and CVSS score 9.8",
|
||||
key: "severity",
|
||||
expected: "CRITICAL",
|
||||
},
|
||||
{
|
||||
testName: "No severity",
|
||||
message: "Component minimist version 0.0.8 with ID npmjs:minimist/0.0.8 violates policy High " +
|
||||
"Vulnerability Security Issue: found vulnerability CVE-2021-44906 with CVSS score 9.8",
|
||||
key: "severity",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testCases {
|
||||
t.Run(c.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := getScore(c.message, c.key)
|
||||
assert.Equal(t, c.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Testing scoreLogicSort function
|
||||
func TestScoreLogicSort(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
testName string
|
||||
leftScore string
|
||||
rightScore string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
testName: "left score is higher",
|
||||
leftScore: "8.8 HIGH",
|
||||
rightScore: "8.1 HIGH",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
testName: "right score is higher",
|
||||
leftScore: "7.9 HIGH",
|
||||
rightScore: "9.3 CRITICAL",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
testName: "left score equals 10.0",
|
||||
leftScore: "10.0 CRITICAL",
|
||||
rightScore: "8.1 HIGH",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
testName: "right score equals 10.0",
|
||||
leftScore: "7.9 HIGH",
|
||||
rightScore: "10.0 CRITICAL",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
testName: "both scores equal 10.0",
|
||||
leftScore: "10.0 CRITICAL",
|
||||
rightScore: "10.0 CRITICAL",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testCases {
|
||||
t.Run(c.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := scoreLogicSort(c.leftScore, c.rightScore)
|
||||
assert.Equal(t, c.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user