github.com/jaylevin/jenkins-library@v1.230.4/cmd/malwareExecuteScan_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  
    12  	v1 "github.com/google/go-containerregistry/pkg/v1"
    13  	"github.com/google/go-containerregistry/pkg/v1/fake"
    14  
    15  	piperDocker "github.com/SAP/jenkins-library/pkg/docker"
    16  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    17  	"github.com/SAP/jenkins-library/pkg/malwarescan"
    18  	"github.com/SAP/jenkins-library/pkg/mock"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  var malwareScanConfig = malwareExecuteScanOptions{
    23  	Host:     "https://example.org/malwarescanner",
    24  	Username: "me",
    25  	Password: "********",
    26  	ScanFile: "target/myFile",
    27  	Timeout:  "60",
    28  }
    29  
    30  type malwareScanUtilsMockBundle struct {
    31  	*mock.FilesMock
    32  
    33  	returnScanResult *malwarescan.ScanResult
    34  	returnSHA256     string
    35  }
    36  
    37  func (utils *malwareScanUtilsMockBundle) SHA256(filePath string) (string, error) {
    38  	if utils.returnSHA256 == "" {
    39  		return utils.returnScanResult.SHA256, nil
    40  	}
    41  
    42  	return utils.returnSHA256, nil
    43  }
    44  
    45  func (utils *malwareScanUtilsMockBundle) OpenFile(path string, flag int, perm os.FileMode) (io.ReadCloser, error) {
    46  	return utils.FilesMock.OpenFile(path, flag, perm)
    47  }
    48  
    49  func (utils *malwareScanUtilsMockBundle) FileWrite(path string, content []byte, perm os.FileMode) error {
    50  	return utils.FilesMock.FileWrite(path, content, perm)
    51  }
    52  
    53  func (utils *malwareScanUtilsMockBundle) Info() (*malwarescan.Info, error) {
    54  	return &malwarescan.Info{EngineVersion: "Mock Malware Scanner", SignatureTimestamp: "n/a"}, nil
    55  }
    56  
    57  func (utils *malwareScanUtilsMockBundle) Scan(candidate io.Reader) (*malwarescan.ScanResult, error) {
    58  	return utils.returnScanResult, nil
    59  }
    60  
    61  func (utils *malwareScanUtilsMockBundle) newDockerClient(options piperDocker.ClientOptions) piperDocker.Download {
    62  	return &dockerClientMock{imageName: options.ImageName, registryURL: options.RegistryURL, localPath: options.LocalPath}
    63  }
    64  
    65  func TestMalwareScanWithNoBuildtool(t *testing.T) {
    66  	files := &mock.FilesMock{}
    67  	files.AddFile("target/myFile", []byte(`HELLO`))
    68  
    69  	utils := malwareScanUtilsMockBundle{
    70  		FilesMock: files,
    71  	}
    72  
    73  	t.Run("No malware, no encrypted content in file", func(t *testing.T) {
    74  		utils.returnScanResult = &malwarescan.ScanResult{
    75  			MalwareDetected:          false,
    76  			EncryptedContentDetected: false,
    77  			ScanSize:                 298782,
    78  			MimeType:                 "application/octet-stream",
    79  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
    80  		}
    81  
    82  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
    83  
    84  		assert.NoError(t, error)
    85  	})
    86  
    87  	t.Run("Malware detected in file", func(t *testing.T) {
    88  		utils.returnScanResult = &malwarescan.ScanResult{
    89  			MalwareDetected:          true,
    90  			EncryptedContentDetected: false,
    91  			ScanSize:                 298782,
    92  			MimeType:                 "application/octet-stream",
    93  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
    94  			Finding:                  "Win.Test.EICAR_HDB-1",
    95  		}
    96  
    97  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
    98  		assert.EqualError(t, error, "Malware scan failed for file 'target/myFile'. Malware detected: true, encrypted content detected: false, finding: Win.Test.EICAR_HDB-1")
    99  	})
   100  
   101  	t.Run("Encrypted content detected in file", func(t *testing.T) {
   102  		utils.returnScanResult = &malwarescan.ScanResult{
   103  			MalwareDetected:          false,
   104  			EncryptedContentDetected: true,
   105  			ScanSize:                 298782,
   106  			MimeType:                 "application/octet-stream",
   107  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
   108  		}
   109  
   110  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   111  		assert.EqualError(t, error, "Malware scan failed for file 'target/myFile'. Malware detected: false, encrypted content detected: true, finding: ")
   112  	})
   113  
   114  	t.Run("Malware and encrypted content detected in file", func(t *testing.T) {
   115  		utils.returnScanResult = &malwarescan.ScanResult{
   116  			MalwareDetected:          true,
   117  			EncryptedContentDetected: true,
   118  			ScanSize:                 298782,
   119  			MimeType:                 "application/octet-stream",
   120  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
   121  			Finding:                  "Win.Test.EICAR_HDB-1",
   122  		}
   123  
   124  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   125  		assert.EqualError(t, error, "Malware scan failed for file 'target/myFile'. Malware detected: true, encrypted content detected: true, finding: Win.Test.EICAR_HDB-1")
   126  	})
   127  
   128  	t.Run("No file and no buildtool specified", func(t *testing.T) {
   129  		malwareScanConfig := malwareExecuteScanOptions{
   130  			Host:     "https://example.org/malwarescanner",
   131  			Username: "me",
   132  			Password: "********",
   133  			Timeout:  "60",
   134  		}
   135  
   136  		error := runMalwareScan(&malwareScanConfig, nil, nil)
   137  		assert.EqualError(t, error, "Please specify a file to be scanned")
   138  	})
   139  
   140  	t.Run("File to be scanned, can't be found", func(t *testing.T) {
   141  		utils.returnScanResult = nil
   142  
   143  		malwareScanConfig := malwareExecuteScanOptions{
   144  			Host:     "https://example.org/malwarescanner",
   145  			Username: "me",
   146  			Password: "********",
   147  			Timeout:  "60",
   148  			ScanFile: "target/fileWhichDoesntExist",
   149  		}
   150  
   151  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   152  		assert.EqualError(t, error, "the file 'target/fileWhichDoesntExist' does not exist: file does not exist")
   153  	})
   154  }
   155  
   156  func TestMalwareScanWithDockerAsBuildtoolTests(t *testing.T) {
   157  	files := &mock.FilesMock{}
   158  	files.AddFile("dockerimagename_latest.tar", []byte(``))
   159  
   160  	utils := malwareScanUtilsMockBundle{
   161  		FilesMock: files,
   162  	}
   163  
   164  	t.Run("No malware detected in docker image", func(t *testing.T) {
   165  		utils.returnScanResult = &malwarescan.ScanResult{
   166  			MalwareDetected:          false,
   167  			EncryptedContentDetected: false,
   168  			ScanSize:                 298782,
   169  			MimeType:                 "application/octet-stream",
   170  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
   171  		}
   172  
   173  		malwareScanConfig := malwareExecuteScanOptions{
   174  			Host:      "https://example.org/malwarescanner",
   175  			Username:  "me",
   176  			Password:  "********",
   177  			Timeout:   "60",
   178  			BuildTool: "docker",
   179  			ScanImage: "dockerimagename:latest",
   180  		}
   181  
   182  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   183  
   184  		assert.NoError(t, error)
   185  	})
   186  
   187  	t.Run("No file and no buildtool specified", func(t *testing.T) {
   188  		malwareScanConfig := malwareExecuteScanOptions{
   189  			Host:     "https://example.org/malwarescanner",
   190  			Username: "me",
   191  			Password: "********",
   192  			Timeout:  "60",
   193  		}
   194  
   195  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   196  		assert.EqualError(t, error, "Please specify a file to be scanned")
   197  	})
   198  }
   199  
   200  func TestMalwareScanWithOtherBuildtoolTests(t *testing.T) {
   201  	files := &mock.FilesMock{}
   202  	files.AddFile("dockerimagename_latest.tar", []byte(``))
   203  
   204  	utils := malwareScanUtilsMockBundle{
   205  		FilesMock: files,
   206  	}
   207  
   208  	t.Run("No malware detected in docker image", func(t *testing.T) {
   209  		utils.returnScanResult = &malwarescan.ScanResult{
   210  			MalwareDetected:          false,
   211  			EncryptedContentDetected: false,
   212  			ScanSize:                 298782,
   213  			MimeType:                 "application/octet-stream",
   214  			SHA256:                   "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85",
   215  		}
   216  
   217  		malwareScanConfig := malwareExecuteScanOptions{
   218  			Host:      "https://example.org/malwarescanner",
   219  			Username:  "me",
   220  			Password:  "********",
   221  			Timeout:   "60",
   222  			BuildTool: "golang",
   223  			ScanImage: "dockerimagename:latest",
   224  		}
   225  
   226  		error := runMalwareScan(&malwareScanConfig, nil, &utils)
   227  
   228  		assert.NoError(t, error)
   229  	})
   230  }
   231  
   232  type httpMock struct {
   233  	Method       string                  // is set during test execution
   234  	URL          string                  // is set before test execution
   235  	ResponseBody string                  // is set before test execution
   236  	Options      piperhttp.ClientOptions // is set during test
   237  	StatusCode   int                     // is set during test
   238  	Body         readCloserMock          // is set during test
   239  }
   240  
   241  func (c *httpMock) SetOptions(options piperhttp.ClientOptions) {
   242  	c.Options = options
   243  }
   244  
   245  func (c *httpMock) SendRequest(method string, url string, r io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) {
   246  	if strings.HasSuffix(url, "/info") {
   247  		return &http.Response{StatusCode: 200, Body: &readCloserMock{Content: "{\"engineVersion\": \"Malware Service Mock\", \"signatureTimestamp\": \"2022-01-12T09:26:28.000Z\"}"}}, nil
   248  	}
   249  
   250  	c.Method = method
   251  	c.URL = url
   252  
   253  	_, err := ioutil.ReadAll(r)
   254  
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	c.Body = readCloserMock{Content: c.ResponseBody}
   260  	res := http.Response{StatusCode: c.StatusCode, Body: &c.Body}
   261  
   262  	return &res, nil
   263  }
   264  
   265  type readCloserMock struct {
   266  	Content string
   267  	Closed  bool
   268  }
   269  
   270  func (rc readCloserMock) Read(b []byte) (n int, err error) {
   271  
   272  	if len(b) < len(rc.Content) {
   273  		// in real life we would fill the buffer according to buffer size ...
   274  		return 0, fmt.Errorf("Buffer size (%d) not sufficient, need: %d", len(b), len(rc.Content))
   275  	}
   276  	copy(b, rc.Content)
   277  	return len(rc.Content), io.EOF
   278  }
   279  
   280  func (rc *readCloserMock) Close() error {
   281  	rc.Closed = true
   282  	return nil
   283  }
   284  
   285  type dockerClientMock struct {
   286  	imageName     string
   287  	registryURL   string
   288  	localPath     string
   289  	includeLayers bool
   290  }
   291  
   292  //DownloadImage download the image to the specified path
   293  func (c *dockerClientMock) DownloadImage(imageSource, filePath string) (v1.Image, error) {
   294  	return &fake.FakeImage{}, nil // fmt.Errorf("%s", filePath)
   295  }
   296  
   297  //DownloadImage download the image to the specified path
   298  func (c *dockerClientMock) DownloadImageContent(imageSource, filePath string) (v1.Image, error) {
   299  	return &fake.FakeImage{}, nil // fmt.Errorf("%s", filePath)
   300  }
   301  
   302  // GetRemoteImageInfo return remote image information
   303  func (c *dockerClientMock) GetRemoteImageInfo(imageSoure string) (v1.Image, error) {
   304  	return &fake.FakeImage{}, nil
   305  }