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