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 }