internal/transformer/events_creator.go (132 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package transformer import ( "context" "fmt" "time" "github.com/elastic/beats/v7/libbeat/beat" libevents "github.com/elastic/beats/v7/libbeat/beat/events" "github.com/elastic/elastic-agent-libs/mapstr" "github.com/gofrs/uuid" "github.com/samber/lo" "github.com/elastic/cloudbeat/internal/config" "github.com/elastic/cloudbeat/internal/dataprovider" "github.com/elastic/cloudbeat/internal/evaluator" "github.com/elastic/cloudbeat/internal/infra/clog" "github.com/elastic/cloudbeat/internal/resources/fetching" ) const ( ecsCategoryConfiguration = "configuration" ecsKindState = "state" ecsTypeInfo = "info" ) const ( EcsOutcomeSuccess = "success" EcsOutcomeFailure = "failure" EcsOutcomeUnknown = "unknown" ) type Transformer struct { log *clog.Logger index string idProvider dataprovider.IdProvider benchmarkDataProvider dataprovider.CommonDataProvider commonDataProvider dataprovider.ElasticCommonDataProvider ruleECSProvider dataprovider.CommonDataProvider } type ECSEvent struct { Category []string `json:"category"` Created time.Time `json:"created"` ID string `json:"id"` Kind string `json:"kind"` Sequence int64 `json:"sequence"` Outcome string `json:"outcome"` Type []string `json:"type"` } type Related struct { Entity []string `json:"entity,omitempty"` } func NewTransformer(log *clog.Logger, cfg *config.Config, bdp dataprovider.CommonDataProvider, cdp dataprovider.ElasticCommonDataProvider, idp dataprovider.IdProvider, rep dataprovider.CommonDataProvider) *Transformer { return &Transformer{ log: log, index: cfg.Datastream(), idProvider: idp, benchmarkDataProvider: bdp, commonDataProvider: cdp, ruleECSProvider: rep, } } func (t *Transformer) CreateBeatEvents(_ context.Context, eventData evaluator.EventData) ([]beat.Event, error) { if len(eventData.Findings) == 0 { return nil, nil } events := make([]beat.Event, 0) resMetadata, err := eventData.GetMetadata() if err != nil { return []beat.Event{}, fmt.Errorf("failed to get resource metadata: %v", err) } id := t.idProvider.GetId(resMetadata.Type, resMetadata.ID) t.log.Infof("resource of type %s with id %s got a new id %s", resMetadata.Type, resMetadata.ID, id) resMetadata.ID = id timestamp := time.Now().UTC() resource := fetching.ResourceFields{ ResourceMetadata: resMetadata, Raw: eventData.RuleResult.Resource, } related := Related{ Entity: lo.Filter(eventData.GetIds(), func(item string, _ int) bool { return item != "" }), } globalEnricher := dataprovider.NewEnricher(t.commonDataProvider) for _, finding := range eventData.Findings { event := beat.Event{ Meta: mapstr.M{libevents.FieldMetaIndex: t.index}, Timestamp: timestamp, Fields: mapstr.M{ "event": BuildECSEvent(eventData.CycleMetadata.Sequence, eventData.Metadata.CreatedAt, []string{ecsCategoryConfiguration}, getEcsOutcome(finding.Result.Evaluation)), "resource": resource, "result": finding.Result, "rule": finding.Rule, "related": related, "message": fmt.Sprintf("Rule %q: %s", finding.Rule.Name, finding.Result.Evaluation), }, } err := t.benchmarkDataProvider.EnrichEvent(&event, resMetadata) if err != nil { return nil, fmt.Errorf("failed to enrich event with benchmark context: %v", err) } err = t.ruleECSProvider.EnrichEvent(&event, resMetadata) if err != nil { return nil, fmt.Errorf("failed to enrich event with rule ECS context: %v", err) } err = globalEnricher.EnrichEvent(&event) if err != nil { return nil, fmt.Errorf("failed to enrich event with global context: %v", err) } err = dataprovider.NewEnricher(eventData).EnrichEvent(&event) if err != nil { return nil, fmt.Errorf("failed to enrich event with resource context: %v", err) } events = append(events, event) } return events, nil } func BuildECSEvent(seq int64, created time.Time, categories []string, outcome string) ECSEvent { id, _ := uuid.NewV4() // zero value in case of an error is uuid.Nil return ECSEvent{ Category: categories, Created: created, ID: id.String(), Kind: ecsKindState, Sequence: seq, Outcome: outcome, Type: []string{ecsTypeInfo}, } } func getEcsOutcome(evaluation string) string { switch evaluation { case "failed": return EcsOutcomeFailure case "passed": return EcsOutcomeSuccess default: return EcsOutcomeUnknown } }