1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/pkg/malwarescan/malwarescan.go

127 lines
3.5 KiB
Go

package malwarescan
import (
"encoding/json"
"fmt"
"io"
"net/http"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/pkg/errors"
)
// ScanResult : Returned by the scan endpoint of the malwarescan api of SAP CP
type ScanResult struct {
MalwareDetected bool `json:"malwareDetected"`
EncryptedContentDetected bool `json:"encryptedContentDetected"`
ScanSize int `json:"scanSize"`
Finding string `json:"finding,omitempty"`
MimeType string `json:"mimeType"`
SHA256 string `json:"SHA256"`
}
// Info : Returned by the info endpoint of the malwarescan api of SAP CP
type Info struct {
MaxScanSize int
SignatureTimestamp string
EngineVersion string
}
// ScanError : Returned by the malwarescan api of SAP CP in case of an error
type ScanError struct {
Message string
}
// Client : Interface for the malwarescan api provided by SAP CP (see https://api.sap.com/api/MalwareScanAPI/overview)
type Client interface {
Scan(candidate io.Reader) (*ScanResult, error)
Info() (*Info, error)
}
// ClientImpl : Client implementation of the malwarescan api provided by SAP CP (see https://api.sap.com/api/MalwareScanAPI/overview)
type ClientImpl struct {
HTTPClient piperhttp.Sender
Host string
}
// Scan : Triggers a malwarescan in SAP CP for the given content.
func (c *ClientImpl) Scan(candidate io.Reader) (*ScanResult, error) {
var scanResult ScanResult
headers := http.Header{}
headers.Add("Content-Type", "application/octet-stream")
err := c.sendAPIRequest("POST", "/scan", candidate, headers, &scanResult)
if err != nil {
return nil, err
}
return &scanResult, nil
}
// Info : Returns some information about the scanengine used by the malwarescan service.
func (c *ClientImpl) Info() (*Info, error) {
var info Info
err := c.sendAPIRequest("GET", "/info", nil, nil, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (c *ClientImpl) sendAPIRequest(method, endpoint string, body io.Reader, header http.Header, obj interface{}) error {
// piper http utils mashall some http response codes into errors. We wan't to check the status code
// ourselves hence we wait with returning that error (maybe also related to errors others than http status codes)
// sendRequest results in any combination of nil and non-nil response and error.
// a response body could even be already closed.
response, err := c.HTTPClient.SendRequest(method, c.Host+endpoint, body, header, nil)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to send request to MalwareService."))
}
if response.StatusCode != 200 {
var scanError ScanError
err = c.unmarshalResponse(response, &scanError)
if err != nil {
return fmt.Errorf("MalwareService returned with status code %d, no further information available", response.StatusCode)
}
return fmt.Errorf("MalwareService returned with status code %d: %s", response.StatusCode, scanError.Message)
}
return c.unmarshalResponse(response, obj)
}
func (c *ClientImpl) readBody(response *http.Response) ([]byte, error) {
if response != nil && response.Body != nil {
defer response.Body.Close()
return io.ReadAll(response.Body)
}
return nil, fmt.Errorf("No response body available")
}
func (c *ClientImpl) unmarshalResponse(response *http.Response, obj interface{}) error {
body, err := c.readBody(response)
if err != nil {
return err
}
err = json.Unmarshal(body, obj)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Unmarshalling of response body failed. Body: '%s'", body))
}
return err
}