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:
parent
b88ebdad6c
commit
ce06b82450
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user