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
|
package agent
import (
"context"
"encoding/json"
"errors"
"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/ioz"
"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))
// 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.
var errs []error
r.log.Info("Creating vulnerabilities in GitLab", logz.VulnerabilitiesCount(len(payloads)))
for _, payload := range payloads {
uuid, err := r.createVulnerability(ctx, payload)
if err != nil {
errs = append(errs, err)
continue
}
uuids = append(uuids, uuid)
}
if len(errs) > 0 {
r.log.Warn("Some vulnerabilities failed to create, skipping vulnerability resolution")
return nil, errors.Join(errs...)
}
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 {
_ = ioz.DiscardData(resp.Body)
return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
var uuidResp 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.DiscardAndClose(resp.Body, &retError)
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
return nil
}
|