From 87e9f17592646d713d75736b806444db433c7dca Mon Sep 17 00:00:00 2001 From: Frank Jogeleit Date: Fri, 6 Sep 2024 13:23:46 +0200 Subject: [PATCH] make securityhub a sync target Signed-off-by: Frank Jogeleit --- charts/policy-reporter/Chart.yaml | 2 +- charts/policy-reporter/README.md | 5 ++- charts/policy-reporter/configs/ui.tmpl | 1 + charts/policy-reporter/values.yaml | 5 ++- pkg/config/resolver.go | 1 + pkg/listener/cleanup.go | 16 ++----- pkg/listener/cleanup_test.go | 2 +- pkg/listener/new_result.go | 24 +++++++++++ pkg/listener/send_result_test.go | 12 +++++- pkg/listener/sync_results.go | 38 +++++++++++++++++ pkg/report/client.go | 3 ++ pkg/target/client.go | 12 +++++- pkg/target/collection.go | 9 +++- pkg/target/collection_test.go | 4 +- pkg/target/discord/discord.go | 4 +- pkg/target/elasticsearch/elasticsearch.go | 4 +- pkg/target/gcs/gcs.go | 4 +- pkg/target/googlechat/googlechat.go | 4 +- pkg/target/kinesis/kinesis.go | 4 +- pkg/target/loki/loki.go | 4 +- pkg/target/s3/s3.go | 4 +- pkg/target/securityhub/securityhub.go | 51 +++++++++++++---------- pkg/target/slack/slack.go | 4 +- pkg/target/teams/teams.go | 4 +- pkg/target/teams/teams_test.go | 2 +- pkg/target/telegram/telegram.go | 4 +- pkg/target/ui/ui.go | 4 +- pkg/target/webhook/webhook.go | 4 +- policy.yaml | 22 ++++++++++ 29 files changed, 185 insertions(+), 72 deletions(-) create mode 100644 pkg/listener/sync_results.go create mode 100644 policy.yaml diff --git a/charts/policy-reporter/Chart.yaml b/charts/policy-reporter/Chart.yaml index 7ef9c585..f589cca4 100644 --- a/charts/policy-reporter/Chart.yaml +++ b/charts/policy-reporter/Chart.yaml @@ -5,7 +5,7 @@ description: | It creates Prometheus Metrics and can send rule validation events to different targets like Loki, Elasticsearch, Slack or Discord type: application -version: 3.0.0-beta.11 +version: 3.0.0-beta.12 appVersion: 3.0.0-beta icon: https://github.com/kyverno/kyverno/raw/main/img/logo.png diff --git a/charts/policy-reporter/README.md b/charts/policy-reporter/README.md index d704abe3..f7a446fe 100644 --- a/charts/policy-reporter/README.md +++ b/charts/policy-reporter/README.md @@ -3,7 +3,7 @@ Policy Reporter watches for PolicyReport Resources. It creates Prometheus Metrics and can send rule validation events to different targets like Loki, Elasticsearch, Slack or Discord -![Version: 3.0.0-beta.11](https://img.shields.io/badge/Version-3.0.0--beta.11-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0-beta](https://img.shields.io/badge/AppVersion-3.0.0--beta-informational?style=flat-square) +![Version: 3.0.0-beta.12](https://img.shields.io/badge/Version-3.0.0--beta.12-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0-beta](https://img.shields.io/badge/AppVersion-3.0.0--beta-informational?style=flat-square) ## Documentation @@ -350,7 +350,7 @@ Check the [Documentation](https://kyverno.github.io/policy-reporter/guide/02-get | ui.image.registry | string | `"ghcr.io"` | Image registry | | ui.image.repository | string | `"kyverno/policy-reporter-ui"` | Image repository | | ui.image.pullPolicy | string | `"IfNotPresent"` | Image PullPolicy | -| ui.image.tag | string | `"2.0.0-beta.12"` | Image tag Defaults to `Chart.AppVersion` if omitted | +| ui.image.tag | string | `"2.0.0-beta.13"` | Image tag Defaults to `Chart.AppVersion` if omitted | | ui.replicaCount | int | `1` | Deployment replica count | | ui.tempDir | string | `"/tmp"` | Temporary Directory to persist session data for authentication | | ui.logging.encoding | string | `"console"` | log encoding possible encodings are console and json | @@ -372,6 +372,7 @@ Check the [Documentation](https://kyverno.github.io/policy-reporter/guide/02-get | ui.oauth.clientSecret | string | `""` | OpenID Connect ClientSecret | | ui.oauth.scopes | list | `[]` | OpenID Connect allowed Scopes | | ui.oauth.secretRef | string | `""` | Provide OpenID Connect configuration via Secret supported keys: `provider`, `clientId`, `clientSecret` | +| ui.banner | string | `""` | optional banner text | | ui.displayMode | string | `""` | DisplayMode dark/light uses the OS configured prefered color scheme as default | | ui.customBoards | list | `[]` | Additional customizable dashboards | | ui.sources | list | `[]` | source specific configurations | diff --git a/charts/policy-reporter/configs/ui.tmpl b/charts/policy-reporter/configs/ui.tmpl index e837dabf..0f3c7532 100644 --- a/charts/policy-reporter/configs/ui.tmpl +++ b/charts/policy-reporter/configs/ui.tmpl @@ -14,6 +14,7 @@ server: ui: displayMode: {{ .Values.ui.displayMode }} + banner: {{ .Values.ui.banner }} {{- $default := false -}} {{- range .Values.ui.clusters }} diff --git a/charts/policy-reporter/values.yaml b/charts/policy-reporter/values.yaml index c507bf4f..89b65f71 100644 --- a/charts/policy-reporter/values.yaml +++ b/charts/policy-reporter/values.yaml @@ -757,7 +757,7 @@ ui: pullPolicy: IfNotPresent # -- (string) Image tag # Defaults to `Chart.AppVersion` if omitted - tag: "2.0.0-beta.12" + tag: "2.0.0-beta.13" # -- Deployment replica count replicaCount: 1 @@ -816,6 +816,9 @@ ui: # supported keys: `provider`, `clientId`, `clientSecret` secretRef: "" + # -- optional banner text + banner: "" + # -- DisplayMode dark/light # uses the OS configured prefered color scheme as default displayMode: "" diff --git a/pkg/config/resolver.go b/pkg/config/resolver.go index b084cda3..a17056bc 100644 --- a/pkg/config/resolver.go +++ b/pkg/config/resolver.go @@ -268,6 +268,7 @@ func (r *Resolver) RegisterSendResultListener() { r.resultListener.RegisterListener(listener.NewSendResultListener(targets)) r.resultListener.RegisterScopeListener(listener.NewSendScopeResultsListener(targets)) + r.resultListener.RegisterSyncListener(listener.NewSendSyncResultsListener(targets)) } // UnregisterSendResultListener resolver method diff --git a/pkg/listener/cleanup.go b/pkg/listener/cleanup.go index 3b91ee82..5206b627 100644 --- a/pkg/listener/cleanup.go +++ b/pkg/listener/cleanup.go @@ -3,8 +3,6 @@ package listener import ( "context" - "github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2" - "github.com/kyverno/policy-reporter/pkg/helper" "github.com/kyverno/policy-reporter/pkg/report" "github.com/kyverno/policy-reporter/pkg/target" ) @@ -13,17 +11,11 @@ const CleanUpListener = "cleanup_listener" func NewCleanupListener(ctx context.Context, targets *target.Collection) report.PolicyReportListener { return func(event report.LifecycleEvent) { - for _, handler := range targets.Clients() { - if event.Type != report.Deleted { - filtered := helper.Filter(event.PolicyReport.GetResults(), func(result v1alpha2.PolicyReportResult) bool { - return handler.Validate(event.PolicyReport, result) - }) - - if len(filtered) == 0 { - return - } - } + if event.Type == report.Added { + return + } + for _, handler := range targets.SyncClients() { handler.CleanUp(ctx, event.PolicyReport) } } diff --git a/pkg/listener/cleanup_test.go b/pkg/listener/cleanup_test.go index ae92666e..bd73665e 100644 --- a/pkg/listener/cleanup_test.go +++ b/pkg/listener/cleanup_test.go @@ -11,7 +11,7 @@ import ( func Test_CleanupListener(t *testing.T) { t.Run("Execute Cleanup Handler", func(t *testing.T) { - c := &client{} + c := &client{cleanup: true} slistener := listener.NewCleanupListener(ctx, target.NewCollection(&target.Target{Client: c})) slistener(report.LifecycleEvent{Type: report.Deleted, PolicyReport: preport1}) diff --git a/pkg/listener/new_result.go b/pkg/listener/new_result.go index 87451d72..1f4eadab 100644 --- a/pkg/listener/new_result.go +++ b/pkg/listener/new_result.go @@ -16,6 +16,7 @@ type ResultListener struct { skipExisting bool listener []report.PolicyReportResultListener scopeListener []report.ScopeResultsListener + syncListener []report.SyncResultsListener cache cache.Cache startUp time.Time } @@ -36,6 +37,14 @@ func (l *ResultListener) UnregisterScopeListener() { l.scopeListener = make([]report.ScopeResultsListener, 0) } +func (l *ResultListener) RegisterSyncListener(listener report.SyncResultsListener) { + l.syncListener = append(l.syncListener, listener) +} + +func (l *ResultListener) UnregisterSyncListener() { + l.syncListener = make([]report.SyncResultsListener, 0) +} + func (l *ResultListener) Listen(event report.LifecycleEvent) { if event.Type != report.Added && event.Type != report.Updated { l.cache.RemoveReport(event.PolicyReport.GetID()) @@ -49,6 +58,21 @@ func (l *ResultListener) Listen(event report.LifecycleEvent) { listenerCount := len(l.listener) scopeListenerCount := len(l.scopeListener) + syncListenerCount := len(l.syncListener) + + if syncListenerCount > 0 { + wg := sync.WaitGroup{} + wg.Add(syncListenerCount) + + for _, cb := range l.syncListener { + go func(callback report.SyncResultsListener) { + defer wg.Done() + + callback(event.PolicyReport) + }(cb) + } + } + if listenerCount == 0 && scopeListenerCount == 0 { l.cache.AddReport(event.PolicyReport) return diff --git a/pkg/listener/send_result_test.go b/pkg/listener/send_result_test.go index 44050a41..8d4b4c74 100644 --- a/pkg/listener/send_result_test.go +++ b/pkg/listener/send_result_test.go @@ -17,6 +17,7 @@ type client struct { validated bool cleanupCalled bool batchSend bool + cleanup bool } func (c *client) Send(result v1alpha2.PolicyReportResult) { @@ -51,8 +52,15 @@ func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReport c.Called = true } -func (c *client) SupportsBatchSend() bool { - return c.batchSend +func (c *client) Type() target.ClientType { + if c.cleanup { + return target.SyncSend + } + if c.batchSend { + return target.BatchSend + } + + return target.SingleSend } func Test_SendResultListener(t *testing.T) { diff --git a/pkg/listener/sync_results.go b/pkg/listener/sync_results.go new file mode 100644 index 00000000..052efa65 --- /dev/null +++ b/pkg/listener/sync_results.go @@ -0,0 +1,38 @@ +package listener + +import ( + "sync" + + "github.com/kyverno/policy-reporter/pkg/crd/api/policyreport/v1alpha2" + "github.com/kyverno/policy-reporter/pkg/helper" + "github.com/kyverno/policy-reporter/pkg/report" + "github.com/kyverno/policy-reporter/pkg/target" +) + +const SendSyncResults = "send_sync_results_listener" + +func NewSendSyncResultsListener(targets *target.Collection) report.SyncResultsListener { + return func(rep v1alpha2.ReportInterface) { + clients := targets.SyncClients() + if len(clients) == 0 { + return + } + + wg := &sync.WaitGroup{} + wg.Add(len(clients)) + + for _, t := range clients { + go func(target target.Client, re v1alpha2.ReportInterface) { + defer wg.Done() + + filtered := helper.Filter(re.GetResults(), func(result v1alpha2.PolicyReportResult) bool { + return target.Validate(re, result) + }) + + target.BatchSend(re, filtered) + }(t, rep) + } + + wg.Wait() + } +} diff --git a/pkg/report/client.go b/pkg/report/client.go index 030f0357..babd440f 100644 --- a/pkg/report/client.go +++ b/pkg/report/client.go @@ -13,6 +13,9 @@ type PolicyReportResultListener = func(v1alpha2.ReportInterface, v1alpha2.Policy // ScopeResultsListener is called whenever a new PolicyReport with a single resource scope and new results comes in type ScopeResultsListener = func(v1alpha2.ReportInterface, []v1alpha2.PolicyReportResult, bool) +// SyncResultsListener is called whenever a PolicyReport event comes in +type SyncResultsListener = func(v1alpha2.ReportInterface) + // PolicyReportClient watches for PolicyReport Events and executes registered callback type PolicyReportClient interface { // Run starts the informer and workerqueue diff --git a/pkg/target/client.go b/pkg/target/client.go index c5ffe335..ac96250c 100644 --- a/pkg/target/client.go +++ b/pkg/target/client.go @@ -13,6 +13,14 @@ import ( "github.com/kyverno/policy-reporter/pkg/validate" ) +type ClientType = string + +const ( + SingleSend ClientType = "single" + BatchSend ClientType = "batch" + SyncSend ClientType = "sync" +) + // Client for a provided Target type Client interface { // Send the given Result to the configured Target @@ -29,8 +37,8 @@ type Client interface { MinimumPriority() string // Sources of the Results which should send to this target, empty means all sources Sources() []string - // SupportsBatchSend for the given target - SupportsBatchSend() bool + // Type for the given target + Type() ClientType // CleanUp old results if supported by the target CleanUp(context.Context, v1alpha2.ReportInterface) } diff --git a/pkg/target/collection.go b/pkg/target/collection.go index fcccdb8e..cbf8657b 100644 --- a/pkg/target/collection.go +++ b/pkg/target/collection.go @@ -80,13 +80,18 @@ func (c *Collection) Client(name string) Client { func (c *Collection) SingleSendClients() []Client { return helper.Filter(c.Clients(), func(c Client) bool { - return !c.SupportsBatchSend() + return c.Type() == SingleSend + }) +} +func (c *Collection) SyncClients() []Client { + return helper.Filter(c.Clients(), func(c Client) bool { + return c.Type() == SyncSend }) } func (c *Collection) BatchSendClients() []Client { return helper.Filter(c.Clients(), func(c Client) bool { - return c.SupportsBatchSend() + return c.Type() == BatchSend }) } diff --git a/pkg/target/collection_test.go b/pkg/target/collection_test.go index 9863c1e1..2929aec7 100644 --- a/pkg/target/collection_test.go +++ b/pkg/target/collection_test.go @@ -75,13 +75,13 @@ func TestCollection(t *testing.T) { t.Run("SingleSendClients only returns clients which do not support batch sending", func(t *testing.T) { for _, c := range collection.SingleSendClients() { - assert.False(t, c.SupportsBatchSend()) + assert.Equal(t, target.SingleSend, c.Type()) } }) t.Run("BatchSendClients only returns clients which do support batch sending", func(t *testing.T) { for _, c := range collection.BatchSendClients() { - assert.True(t, c.SupportsBatchSend()) + assert.Equal(t, target.BatchSend, c.Type()) } }) } diff --git a/pkg/target/discord/discord.go b/pkg/target/discord/discord.go index 3cb3df93..2b1883fb 100644 --- a/pkg/target/discord/discord.go +++ b/pkg/target/discord/discord.go @@ -119,8 +119,8 @@ func (d *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (d *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (d *client) SupportsBatchSend() bool { - return false +func (d *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new loki.client to send Results to Discord diff --git a/pkg/target/elasticsearch/elasticsearch.go b/pkg/target/elasticsearch/elasticsearch.go index b37aba63..d723f740 100644 --- a/pkg/target/elasticsearch/elasticsearch.go +++ b/pkg/target/elasticsearch/elasticsearch.go @@ -102,8 +102,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (e *client) SupportsBatchSend() bool { - return false +func (e *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new elasticsearch.client to send Results to Elasticsearch diff --git a/pkg/target/gcs/gcs.go b/pkg/target/gcs/gcs.go index a9aaa892..657eacce 100644 --- a/pkg/target/gcs/gcs.go +++ b/pkg/target/gcs/gcs.go @@ -67,8 +67,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (c *client) SupportsBatchSend() bool { - return false +func (c *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new GCS.client to send Results to Google Cloud Storage. diff --git a/pkg/target/googlechat/googlechat.go b/pkg/target/googlechat/googlechat.go index 6b7aafaf..fc8430a4 100644 --- a/pkg/target/googlechat/googlechat.go +++ b/pkg/target/googlechat/googlechat.go @@ -224,8 +224,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (e *client) SupportsBatchSend() bool { - return false +func (e *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new loki.client to send Results to Elasticsearch diff --git a/pkg/target/kinesis/kinesis.go b/pkg/target/kinesis/kinesis.go index 483e2ad9..b375d5e8 100644 --- a/pkg/target/kinesis/kinesis.go +++ b/pkg/target/kinesis/kinesis.go @@ -64,8 +64,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (c *client) SupportsBatchSend() bool { - return false +func (c *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new Kinesis.client to send Results to AWS Kinesis compatible source diff --git a/pkg/target/loki/loki.go b/pkg/target/loki/loki.go index 52d4f2ca..7c0cfd82 100644 --- a/pkg/target/loki/loki.go +++ b/pkg/target/loki/loki.go @@ -135,8 +135,8 @@ func (l *client) send(payload payload) { http.ProcessHTTPResponse(l.Name(), resp, err) } -func (l *client) SupportsBatchSend() bool { - return true +func (l *client) Type() target.ClientType { + return target.BatchSend } func (l *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} diff --git a/pkg/target/s3/s3.go b/pkg/target/s3/s3.go index f7f8247f..ecbe29d7 100644 --- a/pkg/target/s3/s3.go +++ b/pkg/target/s3/s3.go @@ -66,8 +66,8 @@ func (c *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (c *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (c *client) SupportsBatchSend() bool { - return false +func (c *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new S3.client to send Results to S3. diff --git a/pkg/target/securityhub/securityhub.go b/pkg/target/securityhub/securityhub.go index c4b8f762..fe65d9fb 100644 --- a/pkg/target/securityhub/securityhub.go +++ b/pkg/target/securityhub/securityhub.go @@ -131,9 +131,9 @@ func (c *client) BatchSend(polr v1alpha2.ReportInterface, results []v1alpha2.Pol return } - list, err := c.getFindingsByIDs(context.Background(), polr.GetSource(), toResourceIDFilter(polr, results)) + list, err := c.getFindingsByIDs(context.Background(), polr.GetSource(), toResourceIDFilter(polr, results), "") if err != nil { - zap.L().Error(c.Name()+": PUSH FAILED", zap.Error(err)) + zap.L().Error(c.Name()+": failed to get findings", zap.Error(err)) return } @@ -153,8 +153,8 @@ func (c *client) BatchSend(polr v1alpha2.ReportInterface, results []v1alpha2.Pol zap.L().Info(c.Name()+": PUSH OK", zap.Int("updated", updated)) } - mapping := make(map[string]bool, len(findings)) - for _, f := range findings { + mapping := make(map[string]bool, len(list)) + for _, f := range list { mapping[*f.Id] = true } @@ -175,25 +175,20 @@ func (c *client) BatchSend(polr v1alpha2.ReportInterface, results []v1alpha2.Pol return } - zap.L().Info(c.Name()+": PUSH OK", zap.Int32("imported", *res.SuccessCount), zap.Int32("failed", *res.FailedCount)) + zap.L().Info(c.Name()+": PUSH OK", zap.Int32("imported", *res.SuccessCount), zap.Int32("failed", *res.FailedCount), zap.String("report", polr.GetKey())) } func (c *client) Sync(ctx context.Context) error { - zap.L().Info("sync?", zap.Bool("sync", c.cleanup)) if !c.cleanup { return nil } - zap.L().Info("start sync") - list, err := c.getFindings(ctx, "") if err != nil { zap.L().Error(c.Name()+": failed to get findings", zap.Error(err)) return err } - zap.L().Info("findings", zap.Int("count", len(list))) - if len(list) == 0 { return nil } @@ -211,8 +206,6 @@ func (c *client) Sync(ctx context.Context) error { return err } - zap.L().Info("finished finding sync") - return nil } @@ -221,19 +214,25 @@ func (c *client) CleanUp(ctx context.Context, report v1alpha2.ReportInterface) { return } + if source := report.GetSource(); source != "" && len(c.BaseClient.Sources()) > 0 { + if !helper.Contains(source, c.BaseClient.Sources()) { + return + } + } + resourceIds := toResourceIDFilter(report, report.GetResults()) if len(resourceIds) == 0 { return } - findings, err := c.getFindingsByIDs(ctx, report.GetSource(), resourceIds) + findings, err := c.getFindingsByIDs(ctx, report.GetSource(), resourceIds, string(types.WorkflowStatusNew)) if err != nil { zap.L().Error(c.Name()+": failed to get findings", zap.Error(err)) return } + defer time.Sleep(c.delay) if len(findings) == 0 { - time.Sleep(c.delay) return } @@ -251,7 +250,6 @@ func (c *client) CleanUp(ctx context.Context, report v1alpha2.ReportInterface) { } if len(mapping) == 0 { - time.Sleep(c.delay) return } @@ -263,14 +261,13 @@ func (c *client) CleanUp(ctx context.Context, report v1alpha2.ReportInterface) { }) } - if _, err := c.batchUpdate(ctx, list, types.WorkflowStatusResolved); err != nil { + count, err := c.batchUpdate(ctx, list, types.WorkflowStatusResolved) + if err != nil { zap.L().Error(c.Name()+": failed to batch archived findings", zap.Error(err)) - time.Sleep(c.delay) return } - zap.L().Info(c.Name()+": Findings updated", zap.Int("count", len(list))) - time.Sleep(c.delay) + zap.L().Info(c.Name()+": CLEANUP OK", zap.Int("count", count), zap.String("report", report.GetKey())) } func (c *client) mapOtherDetails(polr v1alpha2.ReportInterface, result v1alpha2.PolicyReportResult) map[string]string { @@ -369,7 +366,7 @@ func (c *client) batchUpdate(ctx context.Context, findings []types.AwsSecurityFi return updated, nil } -func (c *client) getFindingsByIDs(ctx context.Context, source string, resources []types.StringFilter) ([]types.AwsSecurityFinding, error) { +func (c *client) getFindingsByIDs(ctx context.Context, source string, resources []types.StringFilter, status string) ([]types.AwsSecurityFinding, error) { list := make([]types.AwsSecurityFinding, 0) if len(resources) == 0 { return list, nil @@ -380,6 +377,16 @@ func (c *client) getFindingsByIDs(ctx context.Context, source string, resources for _, res := range chunks { filter := c.BaseFilter(source) filter.ResourceId = res + + if status != "" { + filter.WorkflowStatus = []types.StringFilter{ + { + Comparison: types.StringFilterComparisonEquals, + Value: toPointer(status), + }, + } + } + var token *string for { @@ -448,8 +455,8 @@ func (c *client) BaseFilter(source string) *types.AwsSecurityFindingFilters { } } -func (c *client) SupportsBatchSend() bool { - return true +func (c *client) Type() target.ClientType { + return target.SyncSend } // NewClient creates a new SecurityHub.client to send Results to SecurityHub. diff --git a/pkg/target/slack/slack.go b/pkg/target/slack/slack.go index ef106808..b3723d7c 100644 --- a/pkg/target/slack/slack.go +++ b/pkg/target/slack/slack.go @@ -310,8 +310,8 @@ func (s *client) PostMessage(message *slack.WebhookMessage) { http.ProcessHTTPResponse(s.Name(), resp, err) } -func (s *client) SupportsBatchSend() bool { - return true +func (s *client) Type() target.ClientType { + return target.BatchSend } func (s *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} diff --git a/pkg/target/teams/teams.go b/pkg/target/teams/teams.go index e9a1f309..ede446fb 100644 --- a/pkg/target/teams/teams.go +++ b/pkg/target/teams/teams.go @@ -68,8 +68,8 @@ func (s *client) PostMessage(message *adaptivecard.Message) { http.ProcessHTTPResponse(s.Name(), resp, err) } -func (s *client) SupportsBatchSend() bool { - return true +func (s *client) Type() target.ClientType { + return target.BatchSend } func (s *client) newMessage(resource *corev1.ObjectReference, results []v1alpha2.PolicyReportResult) *adaptivecard.Message { diff --git a/pkg/target/teams/teams_test.go b/pkg/target/teams/teams_test.go index b18c0e2a..0ec1f637 100644 --- a/pkg/target/teams/teams_test.go +++ b/pkg/target/teams/teams_test.go @@ -185,6 +185,6 @@ func Test_TeamsTarget(t *testing.T) { HTTPClient: testClient{}, }) - assert.True(t, client.SupportsBatchSend()) + assert.Equal(t, target.BatchSend, client.Type()) }) } diff --git a/pkg/target/telegram/telegram.go b/pkg/target/telegram/telegram.go index a04ba4d0..567d3b9e 100644 --- a/pkg/target/telegram/telegram.go +++ b/pkg/target/telegram/telegram.go @@ -155,8 +155,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (e *client) SupportsBatchSend() bool { - return false +func (e *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new loki.client to send Results to Elasticsearch diff --git a/pkg/target/ui/ui.go b/pkg/target/ui/ui.go index 2b30d1a6..456b5ed6 100644 --- a/pkg/target/ui/ui.go +++ b/pkg/target/ui/ui.go @@ -35,8 +35,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (e *client) SupportsBatchSend() bool { - return false +func (e *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new loki.client to send Results to Elasticsearch diff --git a/pkg/target/webhook/webhook.go b/pkg/target/webhook/webhook.go index 1515a0fe..7ad7b618 100644 --- a/pkg/target/webhook/webhook.go +++ b/pkg/target/webhook/webhook.go @@ -57,8 +57,8 @@ func (e *client) CleanUp(_ context.Context, _ v1alpha2.ReportInterface) {} func (e *client) BatchSend(_ v1alpha2.ReportInterface, _ []v1alpha2.PolicyReportResult) {} -func (e *client) SupportsBatchSend() bool { - return false +func (e *client) Type() target.ClientType { + return target.SingleSend } // NewClient creates a new loki.client to send Results to Elasticsearch diff --git a/policy.yaml b/policy.yaml new file mode 100644 index 00000000..ed32e280 --- /dev/null +++ b/policy.yaml @@ -0,0 +1,22 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-labels +spec: + validationFailureAction: Audit + rules: + - name: check-for-labels + match: + any: + - resources: + kinds: + - Pod + namespaces: + - policy-reporter + + validate: + message: "label 'app.kubernetes.io/test' is required" + pattern: + metadata: + labels: + app.kubernetes.io/test: "?*" \ No newline at end of file