2020-02-06 17:16:34 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2022-07-12 15:19:12 +02:00
|
|
|
"testing"
|
2020-02-06 17:16:34 +02:00
|
|
|
"time"
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
piperHttp "github.com/SAP/jenkins-library/pkg/http"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/mock"
|
2020-02-06 17:16:34 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/protecode"
|
|
|
|
"github.com/stretchr/testify/assert"
|
2020-08-11 14:42:08 +02:00
|
|
|
"github.com/stretchr/testify/require"
|
2020-02-06 17:16:34 +02:00
|
|
|
|
2022-03-30 13:58:16 +02:00
|
|
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
2022-03-23 11:02:00 +02:00
|
|
|
"github.com/google/go-containerregistry/pkg/v1/fake"
|
2020-02-06 17:16:34 +02:00
|
|
|
)
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
type protecodeTestUtilsBundle struct {
|
|
|
|
*mock.FilesMock
|
|
|
|
*mock.DownloadMock
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunProtecodeScan(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/4486/" || requestURI == "/api/product/4711/" {
|
|
|
|
violations := filepath.Join("testdata/TestProtecode", "protecode_result_violations.json")
|
|
|
|
byteContent, err := ioutil.ReadFile(violations)
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoErrorf(t, err, "failed reading %v", violations)
|
2020-02-06 17:16:34 +02:00
|
|
|
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
|
|
|
|
err = json.Unmarshal(byteContent, &response)
|
|
|
|
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(response)
|
2020-02-06 17:16:34 +02:00
|
|
|
|
|
|
|
} else if requestURI == "/api/fetch/" {
|
|
|
|
violations := filepath.Join("testdata/TestProtecode", "protecode_result_violations.json")
|
|
|
|
byteContent, err := ioutil.ReadFile(violations)
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoErrorf(t, err, "failed reading %v", violations)
|
2020-02-06 17:16:34 +02:00
|
|
|
response := protecode.ResultData{Result: protecode.Result{ProductID: 4486, ReportURL: requestURI}}
|
|
|
|
err = json.Unmarshal(byteContent, &response)
|
|
|
|
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(response)
|
2020-02-06 17:16:34 +02:00
|
|
|
|
|
|
|
} 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
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(&response)
|
|
|
|
if _, err := rw.Write([]byte(b.Bytes())); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
} else {
|
|
|
|
response := protecode.Result{ProductID: 4486, ReportURL: requestURI}
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(&response)
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
2022-07-21 09:04:21 +02:00
|
|
|
if _, err := rw.Write([]byte(b.Bytes())); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
}))
|
|
|
|
|
|
|
|
// Close the server when test finishes
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
po := protecode.Options{ServerURL: server.URL}
|
|
|
|
pc := protecode.Protecode{}
|
|
|
|
pc.SetOptions(po)
|
|
|
|
|
|
|
|
influx := protecodeExecuteScanInflux{}
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
ttt := []struct {
|
|
|
|
name string
|
|
|
|
opts protecodeExecuteScanOptions
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "With tar as scan image",
|
|
|
|
opts: protecodeExecuteScanOptions{
|
|
|
|
ServerURL: server.URL,
|
|
|
|
TimeoutMinutes: "1",
|
|
|
|
VerifyOnly: false,
|
|
|
|
CleanupMode: "none",
|
|
|
|
Group: "13",
|
|
|
|
FetchURL: "/api/fetch/",
|
|
|
|
ExcludeCVEs: "CVE-2018-1, CVE-2017-1000382",
|
|
|
|
ReportFileName: "./cache/report-file.txt",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Without tar as scan image",
|
|
|
|
opts: protecodeExecuteScanOptions{
|
|
|
|
ServerURL: server.URL,
|
|
|
|
ScanImage: "t",
|
|
|
|
TimeoutMinutes: "1",
|
|
|
|
VerifyOnly: false,
|
|
|
|
CleanupMode: "none",
|
|
|
|
Group: "13",
|
|
|
|
ExcludeCVEs: "CVE-2018-1, CVE-2017-1000382",
|
|
|
|
ReportFileName: "./cache/report-file.txt",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
for _, test := range ttt {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
files := mock.FilesMock{}
|
|
|
|
|
|
|
|
httpClient := piperHttp.Client{}
|
|
|
|
httpClient.SetFileUtils(&files)
|
|
|
|
|
|
|
|
pc.SetHttpClient(&httpClient)
|
|
|
|
|
|
|
|
docker := mock.DownloadMock{}
|
|
|
|
docker.Stub = func(imageRef string, dest string) (v1.Image, error) {
|
|
|
|
files.AddFile(dest, []byte(""))
|
|
|
|
return &fake.FakeImage{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
utils := protecodeTestUtilsBundle{DownloadMock: &docker, FilesMock: &files}
|
|
|
|
|
|
|
|
cacheDir, _ := files.TempDir("", "protecode-")
|
|
|
|
files.AddFile(filepath.Join(cacheDir, "t.tar"), []byte(""))
|
|
|
|
|
|
|
|
err := runProtecodeScan(&test.opts, &influx, pc, utils, cacheDir)
|
2020-02-06 17:16:34 +02:00
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
if protecodeExecuteJsonExists, err := files.FileExists("protecodeExecuteScan.json"); assert.NoError(t, err) {
|
|
|
|
assert.True(t, protecodeExecuteJsonExists, "protecodeExecuteScan.json expected")
|
|
|
|
}
|
|
|
|
|
|
|
|
if protecodeVulnsJsonExists, err := files.FileExists("protecodescan_vulns.json"); assert.NoError(t, err) {
|
|
|
|
assert.True(t, protecodeVulnsJsonExists, "protecodescan_vulns.json expected")
|
|
|
|
}
|
|
|
|
|
|
|
|
if userSpecifiedReportExists, err := files.FileExists(test.opts.ReportFileName); assert.NoError(t, err) {
|
|
|
|
assert.True(t, userSpecifiedReportExists, "%s must exist", test.opts.ReportFileName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if cacheExists, err := files.DirExists(cacheDir); assert.NoError(t, err) {
|
|
|
|
assert.True(t, cacheExists, "Whoever calls runProtecodeScan is responsible to cleanup the cache.")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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"},
|
|
|
|
}
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
for i, c := range cases {
|
|
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
|
|
got := handleArtifactVersion(c.version)
|
|
|
|
assert.Equal(t, c.want, got)
|
|
|
|
})
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-31 08:45:00 +02:00
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
func TestCreateProtecodeClient(t *testing.T) {
|
2020-02-06 17:16:34 +02:00
|
|
|
cases := []struct {
|
|
|
|
timeout string
|
|
|
|
}{
|
|
|
|
{""},
|
|
|
|
{"1"},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
2022-03-23 11:02:00 +02:00
|
|
|
t.Run(fmt.Sprintf("With timeout: %s", c.timeout), func(t *testing.T) {
|
|
|
|
config := protecodeExecuteScanOptions{TimeoutMinutes: c.timeout}
|
2020-08-31 08:45:00 +02:00
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
client := createProtecodeClient(&config)
|
|
|
|
assert.NotNil(t, client, "client should not be empty")
|
|
|
|
})
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUploadScanOrDeclareFetch(t *testing.T) {
|
2020-09-02 10:41:12 +02:00
|
|
|
// init
|
2020-02-06 17:16:34 +02:00
|
|
|
testFile, err := ioutil.TempFile("", "testFileUpload")
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoError(t, err)
|
2020-02-06 17:16:34 +02:00
|
|
|
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
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(&response)
|
|
|
|
if _, err := rw.Write([]byte(b.Bytes())); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
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
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(&response)
|
|
|
|
if _, err := rw.Write([]byte(b.Bytes())); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
|
|
|
// 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
|
2021-09-13 11:13:48 +02:00
|
|
|
prID int
|
2020-02-06 17:16:34 +02:00
|
|
|
want int
|
|
|
|
}{
|
2021-09-13 11:13:48 +02:00
|
|
|
{false, "test", "group1", "/api/fetch/", "", "", -1, 4711},
|
|
|
|
{true, "test", "group1", "/api/fetch/", "", "", -1, 4711},
|
|
|
|
{false, "test", "group1", "/api/fetch/", "", "", 4711, 4711},
|
|
|
|
{false, "test", "group1", "/api/fetch/", "", "", 0, 4711},
|
|
|
|
|
|
|
|
{false, "test", "group1", "", path, "", -1, 4711},
|
|
|
|
{true, "test", "group1", "", path, "", -1, 4711},
|
|
|
|
{false, "test", "group1", "", path, "PR_4711", 4711, 4711},
|
|
|
|
{false, "test", "group1", "", path, "", 0, 4711},
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
for i, c := range cases {
|
|
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
|
|
utils := protecodeTestUtilsBundle{
|
|
|
|
FilesMock: &mock.FilesMock{},
|
|
|
|
DownloadMock: &mock.DownloadMock{},
|
|
|
|
}
|
|
|
|
|
|
|
|
// test
|
|
|
|
config := protecodeExecuteScanOptions{VerifyOnly: c.reuse, CleanupMode: c.clean, Group: c.group, FetchURL: c.fetchURL, FilePath: c.filePath}
|
|
|
|
// got := uploadScanOrDeclareFetch(config, 0, pc, fileName)
|
|
|
|
got := uploadScanOrDeclareFetch(utils, config, c.prID, pc, fileName)
|
|
|
|
// assert
|
|
|
|
assert.Equal(t, c.want, got)
|
|
|
|
})
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExecuteProtecodeScan(t *testing.T) {
|
2020-08-31 08:45:00 +02:00
|
|
|
testDataFile := filepath.Join("testdata", "TestProtecode", "protecode_result_violations.json")
|
|
|
|
violationsAbsPath, err := filepath.Abs(testDataFile)
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoErrorf(t, err, "failed to obtain absolute path to test data with violations: %v", err)
|
2020-08-31 08:45:00 +02:00
|
|
|
|
2020-02-06 17:16:34 +02:00
|
|
|
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/" {
|
2020-08-31 08:45:00 +02:00
|
|
|
byteContent, err := ioutil.ReadFile(violationsAbsPath)
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoErrorf(t, err, "failed reading %v", violationsAbsPath)
|
2020-02-06 17:16:34 +02:00
|
|
|
response := protecode.ResultData{}
|
|
|
|
err = json.Unmarshal(byteContent, &response)
|
|
|
|
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(response)
|
2020-02-06 17:16:34 +02:00
|
|
|
|
|
|
|
} else if requestURI == "/api/product/4711/pdf-report" {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
response := protecode.ResultData{Result: protecode.Result{ProductID: 4711, ReportURL: requestURI}}
|
2022-07-21 09:04:21 +02:00
|
|
|
_ = json.NewEncoder(&b).Encode(&response)
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
2022-07-21 09:04:21 +02:00
|
|
|
if _, err := rw.Write([]byte(b.Bytes())); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-02-06 17:16:34 +02:00
|
|
|
}))
|
|
|
|
|
|
|
|
// 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},
|
|
|
|
}
|
|
|
|
|
2020-08-31 08:45:00 +02:00
|
|
|
resetDir, err := os.Getwd()
|
2020-09-02 10:41:12 +02:00
|
|
|
require.NoErrorf(t, err, "Failed to get current directory: %v", err)
|
2020-08-31 08:45:00 +02:00
|
|
|
defer func() { _ = os.Chdir(resetDir) }()
|
|
|
|
|
2022-03-23 11:02:00 +02:00
|
|
|
for i, c := range cases {
|
|
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
|
|
utils := protecodeTestUtilsBundle{
|
|
|
|
FilesMock: &mock.FilesMock{},
|
|
|
|
DownloadMock: &mock.DownloadMock{},
|
|
|
|
}
|
|
|
|
config := protecodeExecuteScanOptions{VerifyOnly: 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"}
|
|
|
|
influxData := &protecodeExecuteScanInflux{}
|
|
|
|
// test
|
|
|
|
executeProtecodeScan(influxData, pc, &config, "dummy", utils)
|
|
|
|
// assert
|
|
|
|
assert.Equal(t, 1125, influxData.protecode_data.fields.historical_vulnerabilities)
|
|
|
|
assert.Equal(t, 0, influxData.protecode_data.fields.triaged_vulnerabilities)
|
|
|
|
assert.Equal(t, 1, influxData.protecode_data.fields.excluded_vulnerabilities)
|
2022-12-19 19:49:59 +02:00
|
|
|
assert.Equal(t, 129, influxData.protecode_data.fields.major_vulnerabilities)
|
2022-03-23 11:02:00 +02:00
|
|
|
assert.Equal(t, 226, influxData.protecode_data.fields.vulnerabilities)
|
|
|
|
})
|
2020-02-06 17:16:34 +02:00
|
|
|
}
|
|
|
|
}
|
2020-08-11 14:42:08 +02:00
|
|
|
|
|
|
|
func TestCorrectDockerConfigEnvVar(t *testing.T) {
|
2020-08-12 14:57:11 +02:00
|
|
|
t.Run("with credentials", func(t *testing.T) {
|
|
|
|
// init
|
2022-07-12 15:19:12 +02:00
|
|
|
testDirectory := t.TempDir()
|
2020-08-12 14:57:11 +02:00
|
|
|
|
|
|
|
dockerConfigDir := filepath.Join(testDirectory, "myConfig")
|
2022-07-21 09:04:21 +02:00
|
|
|
if err := os.Mkdir(dockerConfigDir, 0755); err != nil {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2020-08-12 14:57:11 +02:00
|
|
|
require.DirExists(t, dockerConfigDir)
|
|
|
|
|
|
|
|
dockerConfigFile := filepath.Join(dockerConfigDir, "docker.json")
|
|
|
|
file, _ := os.Create(dockerConfigFile)
|
|
|
|
defer file.Close()
|
|
|
|
require.FileExists(t, dockerConfigFile)
|
|
|
|
|
|
|
|
resetValue := os.Getenv("DOCKER_CONFIG")
|
|
|
|
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
|
|
|
// test
|
|
|
|
correctDockerConfigEnvVar(&protecodeExecuteScanOptions{DockerConfigJSON: dockerConfigFile})
|
|
|
|
// assert
|
|
|
|
absolutePath, _ := filepath.Abs(dockerConfigDir)
|
|
|
|
assert.Equal(t, absolutePath, os.Getenv("DOCKER_CONFIG"))
|
|
|
|
})
|
|
|
|
t.Run("without credentials", func(t *testing.T) {
|
|
|
|
// init
|
|
|
|
resetValue := os.Getenv("DOCKER_CONFIG")
|
|
|
|
defer os.Setenv("DOCKER_CONFIG", resetValue)
|
|
|
|
// test
|
|
|
|
correctDockerConfigEnvVar(&protecodeExecuteScanOptions{})
|
|
|
|
// assert
|
|
|
|
assert.Equal(t, resetValue, os.Getenv("DOCKER_CONFIG"))
|
|
|
|
})
|
2020-08-11 14:42:08 +02:00
|
|
|
}
|