1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-18 05:18:24 +02:00

fix(protecodeExecuteScan): Handling of empty findings (#2818)

* Don't fail if components list is empty.  Resolves failures when scanning images from Crossplane.

* Update formatting with go fmt

* Update pkg/protecode/protecode.go

Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>

* Update pkg/protecode/protecode.go

Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>

* Fix change, make consistent

Co-authored-by: d.small@sap.com <d.small@sap.com>
Co-authored-by: dee0 <dsmallzero@gmail.com>
Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
This commit is contained in:
Sven Merk 2021-05-17 13:38:17 +02:00 committed by GitHub
parent b88ebdad6c
commit ce06b82450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 42 deletions

View File

@ -89,6 +89,14 @@ type Protecode struct {
logger *logrus.Entry
}
// Just calls SetOptions which makes sure logger is set.
// Added to make test code more resilient
func makeProtecode(opts Options) Protecode {
ret := Protecode{}
ret.SetOptions(opts)
return ret
}
//Options struct which can be used to configure the Protecode struct
type Options struct {
ServerURL string
@ -326,6 +334,16 @@ func (pc *Protecode) DeclareFetchURL(cleanupMode, group, fetchURL string) *Resul
return result
}
// 2021-04-20 d :
// Found, via web search, an announcement that the set of status codes is expanding from
// B, R, F
// to
// B, R, F, S, D, P.
// Only R and F indicate work has completed.
func scanInProgress(status string) bool {
return status != statusReady && status != statusFailed
}
//PollForResult polls the protecode scan for the result scan
func (pc *Protecode) PollForResult(productID int, timeOutInMinutes string) ResultData {
@ -351,7 +369,7 @@ func (pc *Protecode) PollForResult(productID int, timeOutInMinutes string) Resul
i = 0
return response
}
if len(response.Result.Components) > 0 && response.Result.Status != statusBusy {
if !scanInProgress(response.Result.Status) {
ticker.Stop()
i = 0
break
@ -363,10 +381,20 @@ func (pc *Protecode) PollForResult(productID int, timeOutInMinutes string) Resul
}
}
if len(response.Result.Components) == 0 || response.Result.Status == statusBusy {
if scanInProgress(response.Result.Status) {
response, err = pc.pullResult(productID)
if err != nil || len(response.Result.Components) == 0 || response.Result.Status == statusBusy {
pc.logger.Fatal("No result after polling")
if len(response.Result.Components) < 1 {
// 2020-04-20 d :
// We are required to scan all images including 3rd party ones.
// We have found that Crossplane makes use docker images that contain no
// executable code.
// So we can no longer treat an empty Components list as an error.
pc.logger.Warn("Protecode scan did not identify any components.")
}
if err != nil || response.Result.Status == statusBusy {
pc.logger.Fatalf("No result after polling err: %v protecode status: %v", err, response.Result.Status)
}
}

View File

@ -15,8 +15,6 @@ import (
"strings"
"time"
piperHttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/stretchr/testify/assert"
)
@ -34,7 +32,7 @@ func TestMapResponse(t *testing.T) {
{`{"results": {"status": "B", "id": 209396, "product_id": 209396, "report_url": "https://protecode.c.eu-de-2.cloud.sap/products/209396/"}}`, new(ResultData), &ResultData{Result: Result{ProductID: 209396, Status: statusBusy, ReportURL: "https://protecode.c.eu-de-2.cloud.sap/products/209396/"}}},
{`{"products": [{"product_id": 1}]}`, new(ProductData), &ProductData{Products: []Product{{ProductID: 1}}}},
}
pc := Protecode{}
pc := makeProtecode(Options{})
for _, c := range cases {
r := ioutil.NopCloser(bytes.NewReader([]byte(c.give)))
@ -63,7 +61,7 @@ func TestParseResultSuccess(t *testing.T) {
},
},
}
pc := Protecode{}
pc := makeProtecode(Options{})
m, vulns := pc.ParseResultForInflux(result, "Excluded CVES: Cve4,")
t.Run("Parse Protecode Results", func(t *testing.T) {
assert.Equal(t, 1, m["historical_vulnerabilities"])
@ -84,7 +82,7 @@ func TestParseResultViolations(t *testing.T) {
if err != nil {
t.Fatalf("failed reading %v", violations)
}
pc := Protecode{}
pc := makeProtecode(Options{})
resultData := new(ResultData)
pc.mapResponse(ioutil.NopCloser(strings.NewReader(string(byteContent))), resultData)
@ -110,7 +108,7 @@ func TestParseResultNoViolations(t *testing.T) {
t.Fatalf("failed reading %v", noViolations)
}
pc := Protecode{}
pc := makeProtecode(Options{})
resultData := new(ResultData)
pc.mapResponse(ioutil.NopCloser(strings.NewReader(string(byteContent))), resultData)
@ -135,7 +133,7 @@ func TestParseResultTriaged(t *testing.T) {
t.Fatalf("failed reading %v", triaged)
}
pc := Protecode{}
pc := makeProtecode(Options{})
resultData := new(ResultData)
pc.mapResponse(ioutil.NopCloser(strings.NewReader(string(byteContent))), resultData)
@ -168,17 +166,14 @@ func TestLoadExistingProductSuccess(t *testing.T) {
// Close the server when test finishes
defer server.Close()
client := &piperHttp.Client{}
client.SetOptions(piperHttp.ClientOptions{})
cases := []struct {
pc Protecode
protecodeGroup string
reuseExisting bool
want int
}{
{Protecode{serverURL: server.URL, client: client, logger: log.Entry().WithField("package", "SAP/jenkins-library/pkg/protecode")}, "group", true, 1},
{Protecode{serverURL: server.URL, client: client}, "group32", false, -1},
{makeProtecode(Options{ServerURL: server.URL}), "group", true, 1},
{makeProtecode(Options{ServerURL: server.URL}), "group32", false, -1},
}
for _, c := range cases {
@ -194,15 +189,24 @@ func TestPollForResultSuccess(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
requestURI = req.RequestURI
productID := 111
// 2021-04-20 d :
// Added case '333' to test proper handling of case where Component list is empty.
if strings.Contains(requestURI, "222") {
productID = 222
} else if strings.Contains(requestURI, "333") {
productID = 333
}
response = ResultData{Result: Result{ProductID: productID, ReportURL: requestURI, Status: "D", Components: []Component{
{Vulns: []Vulnerability{
{Triage: []Triage{{ID: 1}}}},
}},
}}
var cmpnts []Component
if productID != 333 {
cmpnts = []Component{
{Vulns: []Vulnerability{
{Triage: []Triage{{ID: 1}}}},
}}
}
response = ResultData{Result: Result{ProductID: productID, ReportURL: requestURI, Status: "D", Components: cmpnts}}
var b bytes.Buffer
json.NewEncoder(&b).Encode(&response)
@ -224,13 +228,12 @@ func TestPollForResultSuccess(t *testing.T) {
{Triage: []Triage{{ID: 1}}}},
}},
}}},
{333, ResultData{Result: Result{ProductID: 333, ReportURL: "/api/product/333/", Status: "D"}}},
}
// Close the server when test finishes
defer server.Close()
client := &piperHttp.Client{}
client.SetOptions(piperHttp.ClientOptions{})
pc := Protecode{serverURL: server.URL, client: client, duration: (time.Minute * 1), logger: log.Entry().WithField("package", "SAP/jenkins-library/pkg/protecode")}
pc := makeProtecode(Options{ServerURL: server.URL, Duration: (time.Minute * 1)})
for _, c := range cases {
got := pc.PollForResult(c.productID, "1")
@ -264,16 +267,13 @@ func TestPullResultSuccess(t *testing.T) {
// Close the server when test finishes
defer server.Close()
client := &piperHttp.Client{}
client.SetOptions(piperHttp.ClientOptions{})
cases := []struct {
pc Protecode
productID int
want ResultData
}{
{Protecode{serverURL: server.URL, client: client}, 111, ResultData{Result: Result{ProductID: 111, ReportURL: "/api/product/111/"}}},
{Protecode{serverURL: server.URL, client: client}, 222, ResultData{Result: Result{ProductID: 222, ReportURL: "/api/product/222/"}}},
{makeProtecode(Options{ServerURL: server.URL}), 111, ResultData{Result: Result{ProductID: 111, ReportURL: "/api/product/111/"}}},
{makeProtecode(Options{ServerURL: server.URL}), 222, ResultData{Result: Result{ProductID: 222, ReportURL: "/api/product/222/"}}},
}
for _, c := range cases {
@ -306,10 +306,7 @@ func TestDeclareFetchURLSuccess(t *testing.T) {
}))
// Close the server when test finishes
defer server.Close()
pc := Protecode{}
po := Options{ServerURL: server.URL}
pc.SetOptions(po)
pc := makeProtecode(Options{ServerURL: server.URL})
cases := []struct {
cleanupMode string
@ -369,10 +366,7 @@ func TestUploadScanFileSuccess(t *testing.T) {
}))
// Close the server when test finishes
defer server.Close()
pc := Protecode{}
po := Options{ServerURL: server.URL}
pc.SetOptions(po)
pc := makeProtecode(Options{ServerURL: server.URL})
testFile, err := ioutil.TempFile("", "testFileUpload")
if err != nil {
@ -424,10 +418,7 @@ func TestLoadReportSuccess(t *testing.T) {
// Close the server when test finishes
defer server.Close()
client := &piperHttp.Client{}
client.SetOptions(piperHttp.ClientOptions{})
pc := Protecode{serverURL: server.URL, client: client}
pc := makeProtecode(Options{ServerURL: server.URL})
cases := []struct {
productID int
@ -467,7 +458,7 @@ func TestDeleteScanSuccess(t *testing.T) {
// Close the server when test finishes
defer server.Close()
pc := Protecode{}
pc := makeProtecode(Options{})
po := Options{ServerURL: server.URL}
pc.SetOptions(po)