1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/cmd/protecodeExecuteScan_test.go

378 lines
11 KiB
Go
Raw Normal View History

package cmd
import (
"testing"
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"time"
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
"github.com/SAP/jenkins-library/pkg/protecode"
"github.com/stretchr/testify/assert"
)
type DockerClientMock struct {
imageName string
registryURL string
localPath string
includeLayers bool
}
//Download interface for download an image to a local path
type Download interface {
GetImageSource() (string, error)
DownloadImageToPath(imageSource, filePath string) (pkgutil.Image, error)
TarImage(writer io.Writer, image pkgutil.Image) error
}
const (
daemonPrefix = "daemon://"
remotePrefix = "remote://"
)
func (c *DockerClientMock) GetImageSource() (string, error) {
imageSource := c.imageName
if len(c.registryURL) > 0 && len(c.localPath) <= 0 {
registry := c.registryURL
url, _ := url.Parse(c.registryURL)
//remove protocoll from registryURL to get registry
if len(url.Scheme) > 0 {
registry = strings.Replace(c.registryURL, fmt.Sprintf("%v://", url.Scheme), "", 1)
}
if strings.HasSuffix(registry, "/") {
imageSource = fmt.Sprintf("%v%v%v", remotePrefix, registry, c.imageName)
} else {
imageSource = fmt.Sprintf("%v%v/%v", remotePrefix, registry, c.imageName)
}
} else if len(c.localPath) > 0 {
imageSource = c.localPath
if !pkgutil.IsTar(c.localPath) {
imageSource = fmt.Sprintf("%v%v", daemonPrefix, c.localPath)
}
}
if len(imageSource) <= 0 {
return imageSource, fmt.Errorf("There is no image source for the parameters: (Name: %v, Registry: %v, local Path: %v)", c.imageName, c.registryURL, c.localPath)
}
return imageSource, nil
}
//DownloadImageToPath download the image to the specified path
func (c *DockerClientMock) DownloadImageToPath(imageSource, filePath string) (pkgutil.Image, error) {
return pkgutil.Image{}, nil
}
//TarImage write a tar from the given image
func (c *DockerClientMock) TarImage(writer io.Writer, image pkgutil.Image) error {
return nil
}
func TestRunProtecodeScan(t *testing.T) {
requestURI := ""
dir, err := ioutil.TempDir("", "t")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
// clean up tmp dir
defer os.RemoveAll(dir)
testFile, err := ioutil.TempFile(dir, "t.tar")
if err != nil {
t.FailNow()
}
fileName := filepath.Base(testFile.Name())
path := strings.ReplaceAll(testFile.Name(), fileName, "")
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
requestURI = req.RequestURI
var b bytes.Buffer
if requestURI == "/api/product/4486/" || requestURI == "/api/product/4711/" {
violations := filepath.Join("testdata/TestProtecode", "protecode_result_violations.json")
byteContent, err := ioutil.ReadFile(violations)
if err != nil {
t.Fatalf("failed reading %v", violations)
}
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
err = json.Unmarshal(byteContent, &response)
json.NewEncoder(&b).Encode(response)
} else if requestURI == "/api/fetch/" {
violations := filepath.Join("testdata/TestProtecode", "protecode_result_violations.json")
byteContent, err := ioutil.ReadFile(violations)
if err != nil {
t.Fatalf("failed reading %v", violations)
}
response := protecode.ResultData{Result: protecode.Result{ProductID: 4486, ReportURL: requestURI}}
err = json.Unmarshal(byteContent, &response)
json.NewEncoder(&b).Encode(response)
} else if requestURI == "/api/product/4486/pdf-report" {
} else if requestURI == "/api/upload/t.tar" {
response := protecode.ResultData{Result: protecode.Result{ProductID: 4486, ReportURL: requestURI}}
var b bytes.Buffer
json.NewEncoder(&b).Encode(&response)
rw.Write([]byte(b.Bytes()))
} else {
response := protecode.Result{ProductID: 4486, ReportURL: requestURI}
json.NewEncoder(&b).Encode(&response)
}
rw.Write([]byte(b.Bytes()))
}))
// Close the server when test finishes
defer server.Close()
po := protecode.Options{ServerURL: server.URL}
pc := protecode.Protecode{}
pc.SetOptions(po)
dClient := &DockerClientMock{imageName: "t", registryURL: "", localPath: path, includeLayers: false}
influx := protecodeExecuteScanInflux{}
reportPath = dir
cachePath = dir
t.Run("With tar as scan image", func(t *testing.T) {
config := protecodeExecuteScanOptions{ServerURL: server.URL, TimeoutMinutes: "1", ReuseExisting: false, CleanupMode: "none", Group: "13", FetchURL: "/api/fetch/", ExcludeCVEs: "CVE-2018-1, CVE-2017-1000382", ReportFileName: "./cache/report-file.txt"}
err = runProtecodeScan(&config, &influx, dClient)
assert.Nil(t, err, "There should be no Error")
})
t.Run("Without tar as scan image", func(t *testing.T) {
config := protecodeExecuteScanOptions{ServerURL: server.URL, ScanImage: "t", FilePath: path, TimeoutMinutes: "1", ReuseExisting: false, CleanupMode: "none", Group: "13", ExcludeCVEs: "CVE-2018-1, CVE-2017-1000382", ReportFileName: "./cache/report-file.txt"}
err = runProtecodeScan(&config, &influx, dClient)
assert.Nil(t, err, "There should be no Error")
})
}
func TestHandleArtifactVersion(t *testing.T) {
cases := []struct {
version string
want string
}{
{"1.0.0-20200131085038+eeb7c1033339bfd404d21ec5e7dc05c80e9e985e", "1"},
{"2.20.20-20200131085038+eeb7c1033339bfd404d21ec5e7dc05c80e9e985e", "2"},
{"3.20.20-20200131085038+eeb7c1033339bfd404d21ec5e7dc05c80e9e985e", "3"},
{"4.20.20-20200131085038", "4"},
{"5.20.20-20200131085038+", "5"},
{"6.00", "6.00"},
{"7.20.20", "7.20.20"},
}
for _, c := range cases {
got := handleArtifactVersion(c.version)
assert.Equal(t, c.want, got)
}
}
func TestCreateClient(t *testing.T) {
cases := []struct {
timeout string
}{
{""},
{"1"},
}
for _, c := range cases {
config := protecodeExecuteScanOptions{TimeoutMinutes: c.timeout}
client := createClient(&config)
assert.NotNil(t, client, "client should not be empty")
}
}
func TestCreateDockerClient(t *testing.T) {
cases := []struct {
scanImage string
dockerRegistryURL string
filePath string
includeLayers bool
}{
{"test", "url", "path", false},
{"", "", "", true},
}
for _, c := range cases {
config := protecodeExecuteScanOptions{ScanImage: c.scanImage, DockerRegistryURL: c.dockerRegistryURL, FilePath: c.filePath, IncludeLayers: c.includeLayers}
client := createDockerClient(&config)
assert.NotNil(t, client, "client should not be empty")
}
}
var fileContent string
func writeToFileMock(f string, d []byte, p os.FileMode) error {
fileContent = string(d)
return nil
}
func TestWriteReportDataToJSONFile(t *testing.T) {
expected := "{\"target\":\"REPORTFILENAME\",\"mandatory\":true,\"productID\":\"4711\",\"serverUrl\":\"DUMMYURL\",\"count\":\"0\",\"cvss2GreaterOrEqualSeven\":\"4\",\"cvss3GreaterOrEqualSeven\":\"3\",\"excludedVulnerabilities\":\"2\",\"triagedVulnerabilities\":\"0\",\"historicalVulnerabilities\":\"1\",\"Vulnerabilities\":[{\"cve\":\"Vulnerability\",\"cvss\":2.5,\"cvss3_score\":\"5.5\"}]}"
var parsedResult map[string]int = make(map[string]int)
parsedResult["historical_vulnerabilities"] = 1
parsedResult["excluded_vulnerabilities"] = 2
parsedResult["cvss3GreaterOrEqualSeven"] = 3
parsedResult["cvss2GreaterOrEqualSeven"] = 4
parsedResult["vulnerabilities"] = 5
config := protecodeExecuteScanOptions{ServerURL: "DUMMYURL", ReportFileName: "REPORTFILENAME"}
writeReportDataToJSONFile(&config, parsedResult, 4711, []protecode.Vuln{{"Vulnerability", 2.5, "5.5"}}, writeToFileMock)
assert.Equal(t, fileContent, expected, "content should be not empty")
}
func TestUploadScanOrDeclareFetch(t *testing.T) {
testFile, err := ioutil.TempFile("", "testFileUpload")
if err != nil {
t.FailNow()
}
defer os.RemoveAll(testFile.Name()) // clean up
fileName := filepath.Base(testFile.Name())
path := strings.ReplaceAll(testFile.Name(), fileName, "")
requestURI := ""
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
requestURI = req.RequestURI
if requestURI == "/api/fetch/" {
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
var b bytes.Buffer
json.NewEncoder(&b).Encode(&response)
rw.Write([]byte(b.Bytes()))
}
if requestURI == fmt.Sprintf("/api/upload/%v", fileName) || requestURI == fmt.Sprintf("/api/upload/PR_4711_%v", fileName) {
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
var b bytes.Buffer
json.NewEncoder(&b).Encode(&response)
rw.Write([]byte(b.Bytes()))
}
}))
// Close the server when test finishes
defer server.Close()
po := protecode.Options{ServerURL: server.URL}
pc := protecode.Protecode{}
pc.SetOptions(po)
cases := []struct {
reuse bool
clean string
group string
fetchURL string
filePath string
prName string
want int
}{
{false, "test", "group1", "/api/fetch/", "", "", 4711},
{false, "test", "group1", "", path, "", 4711},
{false, "test", "group1", "", path, "PR_4711", 4711},
}
for _, c := range cases {
config := protecodeExecuteScanOptions{ReuseExisting: c.reuse, CleanupMode: c.clean, Group: c.group, FetchURL: c.fetchURL, FilePath: c.filePath}
got := uploadScanOrDeclareFetch(config, 0, pc, fileName)
assert.Equal(t, c.want, got)
}
}
func writeReportToFileMock(resp io.ReadCloser, reportFileName string) error {
return nil
}
func TestExecuteProtecodeScan(t *testing.T) {
requestURI := ""
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
requestURI = req.RequestURI
var b bytes.Buffer
if requestURI == "/api/product/4711/" {
violations := filepath.Join("testdata/TestProtecode", "protecode_result_violations.json")
byteContent, err := ioutil.ReadFile(violations)
if err != nil {
t.Fatalf("failed reading %v", violations)
}
response := protecode.ResultData{}
err = json.Unmarshal(byteContent, &response)
json.NewEncoder(&b).Encode(response)
} else if requestURI == "/api/product/4711/pdf-report" {
} else {
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
json.NewEncoder(&b).Encode(&response)
}
rw.Write([]byte(b.Bytes()))
}))
// Close the server when test finishes
defer server.Close()
po := protecode.Options{ServerURL: server.URL, Duration: time.Minute * 3}
pc := protecode.Protecode{}
pc.SetOptions(po)
cases := []struct {
reuse bool
clean string
group string
fetchURL string
want int
}{
{false, "binary", "group1", "/api/fetch/", 4711},
}
for _, c := range cases {
dir, err := ioutil.TempDir("", "t")
if err != nil {
t.Fatal("Failed to create temporary directory")
}
// clean up tmp dir
defer os.RemoveAll(dir)
reportPath = dir
config := protecodeExecuteScanOptions{ReuseExisting: c.reuse, CleanupMode: c.clean, Group: c.group, FetchURL: c.fetchURL, TimeoutMinutes: "3", ExcludeCVEs: "CVE-2018-1, CVE-2017-1000382", ReportFileName: "./cache/report-file.txt"}
got := executeProtecodeScan(pc, &config, "dummy", writeReportToFileMock)
assert.Equal(t, 1125, got["historical_vulnerabilities"])
assert.Equal(t, 0, got["triaged_vulnerabilities"])
assert.Equal(t, 1, got["excluded_vulnerabilities"])
assert.Equal(t, 129, got["cvss3GreaterOrEqualSeven"])
assert.Equal(t, 13, got["cvss2GreaterOrEqualSeven"])
assert.Equal(t, 226, got["vulnerabilities"])
}
}