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 }