github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/malwarescan/malwarescan.go (about) 1 package malwarescan 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 9 piperhttp "github.com/SAP/jenkins-library/pkg/http" 10 "github.com/pkg/errors" 11 ) 12 13 // ScanResult : Returned by the scan endpoint of the malwarescan api of SAP CP 14 type ScanResult struct { 15 MalwareDetected bool `json:"malwareDetected"` 16 EncryptedContentDetected bool `json:"encryptedContentDetected"` 17 ScanSize int `json:"scanSize"` 18 Finding string `json:"finding,omitempty"` 19 MimeType string `json:"mimeType"` 20 SHA256 string `json:"SHA256"` 21 } 22 23 // Info : Returned by the info endpoint of the malwarescan api of SAP CP 24 type Info struct { 25 MaxScanSize int 26 SignatureTimestamp string 27 EngineVersion string 28 } 29 30 // ScanError : Returned by the malwarescan api of SAP CP in case of an error 31 type ScanError struct { 32 Message string 33 } 34 35 // Client : Interface for the malwarescan api provided by SAP CP (see https://api.sap.com/api/MalwareScanAPI/overview) 36 type Client interface { 37 Scan(candidate io.Reader) (*ScanResult, error) 38 Info() (*Info, error) 39 } 40 41 // ClientImpl : Client implementation of the malwarescan api provided by SAP CP (see https://api.sap.com/api/MalwareScanAPI/overview) 42 type ClientImpl struct { 43 HTTPClient piperhttp.Sender 44 Host string 45 } 46 47 // Scan : Triggers a malwarescan in SAP CP for the given content. 48 func (c *ClientImpl) Scan(candidate io.Reader) (*ScanResult, error) { 49 var scanResult ScanResult 50 51 headers := http.Header{} 52 headers.Add("Content-Type", "application/octet-stream") 53 54 err := c.sendAPIRequest("POST", "/scan", candidate, headers, &scanResult) 55 56 if err != nil { 57 return nil, err 58 } 59 60 return &scanResult, nil 61 62 } 63 64 // Info : Returns some information about the scanengine used by the malwarescan service. 65 func (c *ClientImpl) Info() (*Info, error) { 66 var info Info 67 68 err := c.sendAPIRequest("GET", "/info", nil, nil, &info) 69 70 if err != nil { 71 return nil, err 72 } 73 74 return &info, nil 75 } 76 77 func (c *ClientImpl) sendAPIRequest(method, endpoint string, body io.Reader, header http.Header, obj interface{}) error { 78 // piper http utils mashall some http response codes into errors. We wan't to check the status code 79 // ourselves hence we wait with returning that error (maybe also related to errors others than http status codes) 80 81 // sendRequest results in any combination of nil and non-nil response and error. 82 // a response body could even be already closed. 83 response, err := c.HTTPClient.SendRequest(method, c.Host+endpoint, body, header, nil) 84 if err != nil { 85 return errors.Wrap(err, fmt.Sprintf("Failed to send request to MalwareService.")) 86 } 87 88 if response.StatusCode != 200 { 89 var scanError ScanError 90 91 err = c.unmarshalResponse(response, &scanError) 92 93 if err != nil { 94 return fmt.Errorf("MalwareService returned with status code %d, no further information available", response.StatusCode) 95 } 96 97 return fmt.Errorf("MalwareService returned with status code %d: %s", response.StatusCode, scanError.Message) 98 } 99 100 return c.unmarshalResponse(response, obj) 101 } 102 103 func (c *ClientImpl) readBody(response *http.Response) ([]byte, error) { 104 if response != nil && response.Body != nil { 105 defer response.Body.Close() 106 return io.ReadAll(response.Body) 107 } 108 109 return nil, fmt.Errorf("No response body available") 110 } 111 112 func (c *ClientImpl) unmarshalResponse(response *http.Response, obj interface{}) error { 113 body, err := c.readBody(response) 114 115 if err != nil { 116 return err 117 } 118 119 err = json.Unmarshal(body, obj) 120 121 if err != nil { 122 return errors.Wrap(err, fmt.Sprintf("Unmarshalling of response body failed. Body: '%s'", body)) 123 } 124 125 return err 126 }