mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-14 11:03:09 +02:00
b0e4599d4d
* feat(malwareExecuteScan): add support for scanning docker images * refactoring * print out finding if available * generate toolrecord for malware scan * persist scan report * docs * fix * fix * rollback cmd/init_unix.go * auhenticated pull * fix * fix: report shall be consistent with the api model * gcs upload * fix linter
180 lines
6.1 KiB
Go
180 lines
6.1 KiB
Go
package malwarescan
|
|
|
|
import (
|
|
"fmt"
|
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
|
"github.com/stretchr/testify/assert"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
func TestMalwareServiceScan(t *testing.T) {
|
|
t.Run("Scan without finding", func(t *testing.T) {
|
|
httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"malwareDetected\":false,\"encryptedContentDetected\":false,\"scanSize\":298782,\"mimeType\":\"application/octet-stream\",\"SHA256\":\"96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85\"}"}
|
|
|
|
malwareService := ClientImpl{
|
|
HTTPClient: httpClient,
|
|
Host: "https://example.org/malwarescanner",
|
|
}
|
|
|
|
candidate := readCloserMock{Content: "HELLO"}
|
|
scanResult, err := malwareService.Scan(candidate)
|
|
|
|
if assert.NoError(t, err) {
|
|
assert.True(t, httpClient.Body.Closed)
|
|
|
|
assert.Equal(t, "https://example.org/malwarescanner/scan", httpClient.URL)
|
|
assert.Equal(t, "POST", httpClient.Method)
|
|
|
|
if assert.NotNil(t, httpClient.Header) {
|
|
assert.Equal(t, "application/octet-stream", httpClient.Header.Get("Content-Type"))
|
|
}
|
|
|
|
assert.Equal(t, "application/octet-stream", scanResult.MimeType)
|
|
assert.Equal(t, 298782, scanResult.ScanSize)
|
|
assert.Equal(t, "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85", scanResult.SHA256)
|
|
assert.Equal(t, "", scanResult.Finding)
|
|
assert.False(t, scanResult.MalwareDetected)
|
|
assert.False(t, scanResult.EncryptedContentDetected)
|
|
}
|
|
})
|
|
|
|
t.Run("Scan without finding", func(t *testing.T) {
|
|
httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"malwareDetected\":true,\"encryptedContentDetected\":true,\"scanSize\":298782,\"mimeType\":\"application/octet-stream\",\"SHA256\":\"96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85\", \"finding\": \"Description of the finding\"}"}
|
|
|
|
malwareService := ClientImpl{
|
|
HTTPClient: httpClient,
|
|
Host: "https://example.org/malwarescanner",
|
|
}
|
|
|
|
candidate := readCloserMock{Content: "HELLO"}
|
|
scanResult, err := malwareService.Scan(candidate)
|
|
|
|
if assert.NoError(t, err) {
|
|
assert.True(t, httpClient.Body.Closed)
|
|
|
|
assert.Equal(t, "https://example.org/malwarescanner/scan", httpClient.URL)
|
|
assert.Equal(t, "POST", httpClient.Method)
|
|
|
|
if assert.NotNil(t, httpClient.Header) {
|
|
assert.Equal(t, "application/octet-stream", httpClient.Header.Get("Content-Type"))
|
|
}
|
|
|
|
assert.Equal(t, "application/octet-stream", scanResult.MimeType)
|
|
assert.Equal(t, 298782, scanResult.ScanSize)
|
|
assert.Equal(t, "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85", scanResult.SHA256)
|
|
assert.Equal(t, "Description of the finding", scanResult.Finding)
|
|
assert.True(t, scanResult.MalwareDetected)
|
|
assert.True(t, scanResult.EncryptedContentDetected)
|
|
}
|
|
})
|
|
|
|
t.Run("Scan results in error - file to large", func(t *testing.T) {
|
|
httpClient := &httpMock{StatusCode: 413, ResponseBody: "{\"message\":\"Payload too large - The file is too large and cannot be scanned or the archive structure is too complex.\"}"}
|
|
|
|
malwareService := ClientImpl{
|
|
HTTPClient: httpClient,
|
|
Host: "https://example.org/malwarescanner",
|
|
}
|
|
|
|
candidate := readCloserMock{Content: "HELLO"}
|
|
scanResult, err := malwareService.Scan(candidate)
|
|
|
|
assert.Nil(t, scanResult)
|
|
assert.EqualError(t, err, "MalwareService returned with status code 413: Payload too large - The file is too large and cannot be scanned or the archive structure is too complex.")
|
|
})
|
|
|
|
t.Run("Scan results in error - unexpected error", func(t *testing.T) {
|
|
httpClient := &httpMock{StatusCode: 500, ResponseBody: ""}
|
|
|
|
malwareService := ClientImpl{
|
|
HTTPClient: httpClient,
|
|
Host: "https://example.org/malwarescanner",
|
|
}
|
|
|
|
candidate := readCloserMock{Content: "HELLO"}
|
|
scanResult, err := malwareService.Scan(candidate)
|
|
|
|
assert.Nil(t, scanResult)
|
|
assert.EqualError(t, err, "MalwareService returned with status code 500, no further information available")
|
|
})
|
|
}
|
|
|
|
func TestMalwareServiceInfo(t *testing.T) {
|
|
t.Run("Receives engine info", func(t *testing.T) {
|
|
httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"engineVersion\": \"Malware Service Mock\", \"signatureTimestamp\": \"2022-01-12T09:26:28.000Z\", \"maxScanSize\": 666}"}
|
|
|
|
malwareService := ClientImpl{
|
|
HTTPClient: httpClient,
|
|
Host: "https://example.org/malwarescanner",
|
|
}
|
|
|
|
info, err := malwareService.Info()
|
|
|
|
if assert.NoError(t, err) {
|
|
assert.True(t, httpClient.Body.Closed)
|
|
|
|
assert.Equal(t, "https://example.org/malwarescanner/info", httpClient.URL)
|
|
assert.Equal(t, "GET", httpClient.Method)
|
|
assert.Equal(t, "Malware Service Mock", info.EngineVersion)
|
|
assert.Equal(t, "2022-01-12T09:26:28.000Z", info.SignatureTimestamp)
|
|
assert.Equal(t, 666, info.MaxScanSize)
|
|
}
|
|
})
|
|
}
|
|
|
|
type httpMock struct {
|
|
Method string // is set during test execution
|
|
URL string // is set before test execution
|
|
ResponseBody string // is set before test execution
|
|
Options piperhttp.ClientOptions // is set during test
|
|
StatusCode int // is set during test
|
|
Body readCloserMock // is set during test
|
|
Header http.Header // is set during test
|
|
}
|
|
|
|
func (c *httpMock) SetOptions(options piperhttp.ClientOptions) {
|
|
c.Options = options
|
|
}
|
|
|
|
func (c *httpMock) SendRequest(method string, url string, r io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
|
|
c.Method = method
|
|
c.URL = url
|
|
c.Header = header
|
|
|
|
if r != nil {
|
|
_, err := ioutil.ReadAll(r)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
c.Body = readCloserMock{Content: c.ResponseBody}
|
|
res := http.Response{StatusCode: c.StatusCode, Body: &c.Body}
|
|
|
|
return &res, nil
|
|
}
|
|
|
|
type readCloserMock struct {
|
|
Content string
|
|
Closed bool
|
|
}
|
|
|
|
func (rc readCloserMock) Read(b []byte) (n int, err error) {
|
|
|
|
if len(b) < len(rc.Content) {
|
|
// in real life we would fill the buffer according to buffer size ...
|
|
return 0, fmt.Errorf("Buffer size (%d) not sufficient, need: %d", len(b), len(rc.Content))
|
|
}
|
|
copy(b, rc.Content)
|
|
return len(rc.Content), io.EOF
|
|
}
|
|
|
|
func (rc *readCloserMock) Close() error {
|
|
rc.Closed = true
|
|
return nil
|
|
}
|