1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
package agent
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/module/modagent"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/errz"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v16/internal/tool/logz"
"go.uber.org/zap"
)
type Reporter struct {
log *zap.Logger
api modagent.Api
}
type uuidResponse struct {
UUID string `json:"uuid"`
}
type reqBody struct {
UUIDs []string `json:"uuids"`
}
func NewReporter(log *zap.Logger, api modagent.Api) *Reporter {
return &Reporter{
log: log,
api: api,
}
}
func (r *Reporter) Transmit(ctx context.Context, payloads []*Payload) ([]string, error) {
uuids := make([]string, 0, len(payloads))
var firstErr error
// If at least one of the detected vulnerabilities fails to create, don't
// resolve vulnerabilities. Potentially all detected vulnerabilities might
// fail to create, and in turn all previously detected vulnerabilities would
// get resolved otherwise.
failedToCreate := false
r.log.Info("Creating vulnerabilities in GitLab", logz.VulnerabilitiesCount(len(payloads)))
for _, payload := range payloads {
uuid, err := r.createVulnerability(ctx, payload)
if err != nil {
r.log.Error("Failed to create vulnerability", logz.Error(err))
failedToCreate = true
if firstErr == nil {
firstErr = err
}
continue
}
uuids = append(uuids, uuid)
}
if failedToCreate {
r.log.Warn("Some vulnerabilities failed to create, skipping vulnerability resolution")
return nil, firstErr
}
if len(uuids) == 0 {
r.log.Debug("No UUIDs collected, nothing to resolve")
return nil, nil
}
return uuids, nil
}
func (r *Reporter) createVulnerability(ctx context.Context, payload *Payload) (uuid string, retError error) {
resp, err := r.api.MakeGitLabRequest(ctx, "/",
modagent.WithRequestMethod(http.MethodPut),
modagent.WithJsonRequestBody(payload),
)
if err != nil {
return "", fmt.Errorf("error making api request: %w", err)
}
defer errz.SafeClose(resp.Body, &retError)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
uuidResp := new(uuidResponse)
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %w", err)
}
err = json.Unmarshal(body, &uuidResp)
if err != nil {
return "", fmt.Errorf("error parsing response body: %w", err)
}
return uuidResp.UUID, nil
}
func (r *Reporter) ResolveVulnerabilities(ctx context.Context, uuids []string) (retError error) {
body := reqBody{
UUIDs: uuids,
}
resp, err := r.api.MakeGitLabRequest(ctx, "/scan_result",
modagent.WithRequestMethod(http.MethodPost),
modagent.WithJsonRequestBody(body),
)
if err != nil {
return fmt.Errorf("error making api request: %w", err)
}
defer errz.SafeClose(resp.Body, &retError)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
return nil
}
|