diff --git a/.github/workflows/dapr-pubsub.yaml b/.github/workflows/dapr-pubsub.yaml index bb5cd844540..e6f9e798f42 100644 --- a/.github/workflows/dapr-pubsub.yaml +++ b/.github/workflows/dapr-pubsub.yaml @@ -1,18 +1,18 @@ -name: dapr-pubsub +name: dapr-export on: push: paths: - - "pkg/pubsub/dapr" - - "test/pubsub/**" + - "pkg/export/dapr" + - "test/export/**" pull_request: paths: - - "pkg/pubsub/dapr" - - "test/pubsub/**" + - "pkg/export/dapr" + - "test/export/**" permissions: read-all jobs: dapr_test: - name: "Dapr pubsub test" + name: "Dapr export test" runs-on: ubuntu-22.04 timeout-minutes: 15 strategy: @@ -50,20 +50,20 @@ jobs: kind load docker-image --name kind gatekeeper-e2e:latest gatekeeper-crds:latest kubectl create ns gatekeeper-system make e2e-publisher-deploy - make e2e-helm-deploy HELM_REPO=gatekeeper-e2e HELM_CRD_REPO=gatekeeper-crds HELM_RELEASE=latest ENABLE_PUBSUB=true LOG_LEVEL=DEBUG - make test-e2e ENABLE_PUBSUB_TESTS=1 + make e2e-helm-deploy HELM_REPO=gatekeeper-e2e HELM_CRD_REPO=gatekeeper-crds HELM_RELEASE=latest ENABLE_EXPORT=true LOG_LEVEL=DEBUG + make test-e2e ENABLE_EXPORT_TESTS=1 - name: Save logs if: ${{ always() }} run: | kubectl logs -n fake-subscriber -l app=sub --tail=-1 > logs-audit-subscribe.json - kubectl logs -n gatekeeper-system -l control-plane=audit-controller --tail=-1 > logs-audit-publish.json + kubectl logs -n gatekeeper-system -l control-plane=audit-controller --tail=-1 > logs-audit-export.json - name: Upload artifacts uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 if: ${{ always() }} with: - name: pubsub-logs + name: export-logs path: | logs-*.json diff --git a/Makefile b/Makefile index 7d2ea03213b..e20daca4c59 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ PUSH_TO_GHCR ?= false DEV_TAG ?= dev USE_LOCAL_IMG ?= false ENABLE_GENERATOR_EXPANSION ?= false -ENABLE_PUBSUB ?= false +ENABLE_EXPORT ?= false AUDIT_CONNECTION ?= "audit" AUDIT_CHANNEL ?= "audit" LOG_LEVEL ?= "INFO" @@ -203,7 +203,7 @@ e2e-helm-install: ./.staging/helm/linux-amd64/helm version --client e2e-helm-deploy: e2e-helm-install -ifeq ($(ENABLE_PUBSUB),true) +ifeq ($(ENABLE_EXPORT),true) ./.staging/helm/linux-amd64/helm install manifest_staging/charts/gatekeeper --name-template=gatekeeper \ --namespace ${GATEKEEPER_NAMESPACE} \ --debug --wait \ @@ -220,7 +220,7 @@ ifeq ($(ENABLE_PUBSUB),true) --set auditEventsInvolvedNamespace=true \ --set disabledBuiltins={http.send} \ --set logMutations=true \ - --set audit.enablePubsub=${ENABLE_PUBSUB} \ + --set audit.exportViolations=${ENABLE_EXPORT} \ --set audit.connection=${AUDIT_CONNECTION} \ --set audit.channel=${AUDIT_CHANNEL} \ --set-string auditPodAnnotations.dapr\\.io/enabled=true \ @@ -292,17 +292,17 @@ e2e-helm-upgrade: --set mutationAnnotations=true;\ e2e-subscriber-build-load-image: - docker buildx build --platform="linux/amd64" -t ${FAKE_SUBSCRIBER_IMAGE} --load -f test/pubsub/fake-subscriber/Dockerfile test/pubsub/fake-subscriber + docker buildx build --platform="linux/amd64" -t ${FAKE_SUBSCRIBER_IMAGE} --load -f test/export/fake-subscriber/Dockerfile test/export/fake-subscriber kind load docker-image --name kind ${FAKE_SUBSCRIBER_IMAGE} e2e-subscriber-deploy: kubectl create ns fake-subscriber kubectl get secret redis --namespace=default -o yaml | sed 's/namespace: .*/namespace: fake-subscriber/' | kubectl apply -f - - kubectl apply -f test/pubsub/fake-subscriber/manifest/subscriber.yaml + kubectl apply -f test/export/fake-subscriber/manifest/subscriber.yaml e2e-publisher-deploy: kubectl get secret redis --namespace=default -o yaml | sed 's/namespace: .*/namespace: gatekeeper-system/' | kubectl apply -f - - kubectl apply -f test/pubsub/publish-components.yaml + kubectl apply -f test/export/publish-components.yaml # Build manager binary manager: generate diff --git a/cmd/build/helmify/kustomize-for-helm.yaml b/cmd/build/helmify/kustomize-for-helm.yaml index 3ed0fafd121..8ee832381ff 100644 --- a/cmd/build/helmify/kustomize-for-helm.yaml +++ b/cmd/build/helmify/kustomize-for-helm.yaml @@ -183,7 +183,7 @@ spec: - --operation=audit - --operation=status - --operation=generate - - HELMSUBST_DEPLOYMENT_AUDIT_PUBSUB_ARGS + - HELMSUBST_DEPLOYMENT_AUDIT_EXPORT_ARGS - HELMSUBST_MUTATION_STATUS_ENABLED_ARG - --logtostderr - --health-addr=:HELMSUBST_DEPLOYMENT_AUDIT_HEALTH_PORT diff --git a/cmd/build/helmify/replacements.go b/cmd/build/helmify/replacements.go index 8748ba62433..9334551f88f 100644 --- a/cmd/build/helmify/replacements.go +++ b/cmd/build/helmify/replacements.go @@ -118,8 +118,8 @@ var replacements = map[string]string{ - --default-wait-for-vapb-generation={{ .Values.defaultWaitForVAPBGeneration }} {{- end }}`, - "- HELMSUBST_DEPLOYMENT_AUDIT_PUBSUB_ARGS": `{{ if hasKey .Values.audit "enablePubsub" }} - - --enable-pub-sub={{ .Values.audit.enablePubsub }} + "- HELMSUBST_DEPLOYMENT_AUDIT_EXPORT_ARGS": `{{ if hasKey .Values.audit "exportViolations" }} + - --export-violations={{ .Values.audit.exportViolations }} {{- end }} {{ if hasKey .Values.audit "connection" }} - --audit-connection={{ .Values.audit.connection }} diff --git a/cmd/build/helmify/static/README.md b/cmd/build/helmify/static/README.md index ff24c055568..e19d314ed7a 100644 --- a/cmd/build/helmify/static/README.md +++ b/cmd/build/helmify/static/README.md @@ -221,9 +221,9 @@ information._ | audit.readinessTimeout | Timeout in seconds for audit's readiness probe | `1` | | audit.livenessTimeout | Timeout in seconds for the audit's liveness probe | `1` | | audit.logLevel | The minimum log level for audit, takes precedence over `logLevel` when specified | `null` | -| audit.enablePubsub | (alpha) Enabled pubsub to publish messages | `false` | -| audit.connection | (alpha) Connection name for publishing audit violation messages | `audit-connection` | -| audit.channel | (alpha) Channel name for publishing audit violation messages | `audit-channel` | +| audit.exportViolations | (alpha) Enable exporting violations to external systems | `false` | +| audit.connection | (alpha) Connection name for exporting audit violation messages | `audit-connection` | +| audit.channel | (alpha) Channel name for exporting audit violation messages | `audit-channel` | | replicas | The number of Gatekeeper replicas to deploy for the webhook | `3` | | podAnnotations | The annotations to add to the Gatekeeper pods | `container.seccomp.security.alpha.kubernetes.io/manager: runtime/default` | | podLabels | The labels to add to the Gatekeeper pods | `{}` | diff --git a/manifest_staging/charts/gatekeeper/README.md b/manifest_staging/charts/gatekeeper/README.md index ff24c055568..e19d314ed7a 100644 --- a/manifest_staging/charts/gatekeeper/README.md +++ b/manifest_staging/charts/gatekeeper/README.md @@ -221,9 +221,9 @@ information._ | audit.readinessTimeout | Timeout in seconds for audit's readiness probe | `1` | | audit.livenessTimeout | Timeout in seconds for the audit's liveness probe | `1` | | audit.logLevel | The minimum log level for audit, takes precedence over `logLevel` when specified | `null` | -| audit.enablePubsub | (alpha) Enabled pubsub to publish messages | `false` | -| audit.connection | (alpha) Connection name for publishing audit violation messages | `audit-connection` | -| audit.channel | (alpha) Channel name for publishing audit violation messages | `audit-channel` | +| audit.exportViolations | (alpha) Enable exporting violations to external systems | `false` | +| audit.connection | (alpha) Connection name for exporting audit violation messages | `audit-connection` | +| audit.channel | (alpha) Channel name for exporting audit violation messages | `audit-channel` | | replicas | The number of Gatekeeper replicas to deploy for the webhook | `3` | | podAnnotations | The annotations to add to the Gatekeeper pods | `container.seccomp.security.alpha.kubernetes.io/manager: runtime/default` | | podLabels | The labels to add to the Gatekeeper pods | `{}` | diff --git a/manifest_staging/charts/gatekeeper/templates/gatekeeper-audit-deployment.yaml b/manifest_staging/charts/gatekeeper/templates/gatekeeper-audit-deployment.yaml index e525d64e21e..c360569ef3e 100644 --- a/manifest_staging/charts/gatekeeper/templates/gatekeeper-audit-deployment.yaml +++ b/manifest_staging/charts/gatekeeper/templates/gatekeeper-audit-deployment.yaml @@ -71,8 +71,8 @@ spec: - --operation=audit - --operation=status - --operation=generate - {{ if hasKey .Values.audit "enablePubsub" }} - - --enable-pub-sub={{ .Values.audit.enablePubsub }} + {{ if hasKey .Values.audit "exportViolations" }} + - --export-violations={{ .Values.audit.exportViolations }} {{- end }} {{ if hasKey .Values.audit "connection" }} - --audit-connection={{ .Values.audit.connection }} diff --git a/pkg/audit/manager.go b/pkg/audit/manager.go index 324b43f9443..87fface6a6e 100644 --- a/pkg/audit/manager.go +++ b/pkg/audit/manager.go @@ -67,8 +67,8 @@ var ( auditEventsInvolvedNamespace = flag.Bool("audit-events-involved-namespace", false, "emit audit events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Audit events from cluster-scoped resources will still follow the default behavior") auditMatchKindOnly = flag.Bool("audit-match-kind-only", false, "only use kinds specified in all constraints for auditing cluster resources. if kind is not specified in any of the constraints, it will audit all resources (same as setting this flag to false)") apiCacheDir = flag.String("api-cache-dir", defaultAPICacheDir, "The directory where audit from api server cache are stored, defaults to /tmp/audit") - auditConnection = flag.String("audit-connection", defaultConnection, "(alpha) Connection name for publishing audit violation messages. Defaults to audit-connection") - auditChannel = flag.String("audit-channel", defaultChannel, "(alpha) Channel name for publishing audit violation messages. Defaults to audit-channel") + auditConnection = flag.String("audit-connection", defaultConnection, "(alpha) Connection name for exporting audit violation messages. Defaults to audit-connection") + auditChannel = flag.String("audit-channel", defaultChannel, "(alpha) Channel name for exporting audit violation messages. Defaults to audit-channel") emptyAuditResults = newLimitQueue(0) logStatsAudit = flag.Bool("log-stats-audit", false, "(alpha) log stats metrics for the audit run") ) @@ -106,7 +106,7 @@ type StatusViolation struct { EnforcementActions []string `json:"enforcementActions,omitempty"` } -// ConstraintMsg represents publish message for each constraint. +// ExportMsg represents export message for each violation. type ExportMsg struct { ID string `json:"id,omitempty"` Details interface{} `json:"details,omitempty"` diff --git a/pkg/controller/export/export_config_controller.go b/pkg/controller/export/export_config_controller.go index 1da8a33396f..11f57c484b8 100644 --- a/pkg/controller/export/export_config_controller.go +++ b/pkg/controller/export/export_config_controller.go @@ -25,8 +25,8 @@ import ( ) var ( - ExportEnabled = flag.Bool("enable-pub-sub", false, "(alpha) Enabled pubsub to publish messages") - log = logf.Log.WithName("controller").WithValues(logging.Process, "pubsub_controller") + ExportEnabled = flag.Bool("export-violations", false, "(alpha) Enable exporting violations to external systems") + log = logf.Log.WithName("controller").WithValues(logging.Process, "export_controller") ) type Adder struct { @@ -37,7 +37,7 @@ func (a *Adder) Add(mgr manager.Manager) error { if !*ExportEnabled { return nil } - log.Info("Warning: Alpha flag enable-pub-sub is set to true. This flag may change in the future.") + log.Info("Warning: Alpha flag export-violations is set to true. This flag may change in the future.") r := newReconciler(mgr, a.ExportSystem) return add(mgr, r) } diff --git a/pkg/controller/export/export_config_controller_test.go b/pkg/controller/export/export_config_controller_test.go index 791468a4c9c..0a27840c26c 100644 --- a/pkg/controller/export/export_config_controller_test.go +++ b/pkg/controller/export/export_config_controller_test.go @@ -25,7 +25,7 @@ func TestReconcile(t *testing.T) { t.Fatalf("Unexpected error parsing flag: %v", err) } - err = flag.CommandLine.Parse([]string{"--enable-pub-sub", "true"}) + err = flag.CommandLine.Parse([]string{"--export-violations", "true"}) if err != nil { t.Fatalf("Unexpected error parsing flag: %v", err) } diff --git a/pkg/export/system_test.go b/pkg/export/system_test.go index 5daa62df3e0..df9c75f7e72 100644 --- a/pkg/export/system_test.go +++ b/pkg/export/system_test.go @@ -211,7 +211,7 @@ func TestSystem_Publish(t *testing.T) { wantErr: true, }, { - name: "Publishing to a connection that does not exist", + name: "Exporting to a connection that does not exist", fields: fields{ connections: map[string]string{"audit": dapr.Name}, }, @@ -219,7 +219,7 @@ func TestSystem_Publish(t *testing.T) { wantErr: true, }, { - name: "Publishing to a connection that does exist", + name: "Exporting to a connection that does exist", fields: fields{ connections: testSystem.connectionToDriver, }, diff --git a/test/bats/test.bats b/test/bats/test.bats index 708b0dacaeb..1f2a29698a0 100644 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -661,23 +661,23 @@ __expansion_audit_test() { run kubectl delete --ignore-not-found -f test/expansion/expand_pod_cronjob.yaml } -@test "gatekeeper pubsub test" { - if [ -z $ENABLE_PUBSUB_TESTS ]; then - skip "skipping pubsub tests" +@test "gatekeeper export_violation test" { + if [ -z $ENABLE_EXPORT_TESTS ]; then + skip "skipping export tests" fi run kubectl create ns nginx - run kubectl create -f test/pubsub/nginx_deployment.yaml + run kubectl create -f test/export/nginx_deployment.yaml - run kubectl apply -f test/pubsub/k8srequiredlabels_ct.yaml - run kubectl apply -f test/pubsub/pod_must_have_test.yaml + run kubectl apply -f test/export/k8srequiredlabels_ct.yaml + run kubectl apply -f test/export/pod_must_have_test.yaml wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "constraint_enforced k8srequiredlabels pod-must-have-test" wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "total_violations" - run kubectl delete -f test/pubsub/k8srequiredlabels_ct.yaml --ignore-not-found - run kubectl delete -f test/pubsub/pod_must_have_test.yaml --ignore-not-found - run kubectl delete -f test/pubsub/nginx_deployment.yaml --ignore-not-found + run kubectl delete -f test/export/k8srequiredlabels_ct.yaml --ignore-not-found + run kubectl delete -f test/export/pod_must_have_test.yaml --ignore-not-found + run kubectl delete -f test/export/nginx_deployment.yaml --ignore-not-found run kubectl delete ns nginx --ignore-not-found } diff --git a/test/pubsub/fake-subscriber/Dockerfile b/test/export/fake-subscriber/Dockerfile similarity index 83% rename from test/pubsub/fake-subscriber/Dockerfile rename to test/export/fake-subscriber/Dockerfile index 05b9cb0e837..fc922cb76cb 100644 --- a/test/pubsub/fake-subscriber/Dockerfile +++ b/test/export/fake-subscriber/Dockerfile @@ -12,7 +12,7 @@ ENV GO111MODULE=on \ GOARCH=${TARGETARCH} \ GOARM=${TARGETVARIANT} -WORKDIR /go/src/github.com/open-policy-agent/gatekeeper/test/pubsub/fake-subscriber +WORKDIR /go/src/github.com/open-policy-agent/gatekeeper/test/export/fake-subscriber COPY . . @@ -24,7 +24,7 @@ FROM gcr.io/distroless/static-debian12@sha256:8dd8d3ca2cf283383304fd45a5c9c74d5f WORKDIR / -COPY --from=builder /go/src/github.com/open-policy-agent/gatekeeper/test/pubsub/fake-subscriber/main . +COPY --from=builder /go/src/github.com/open-policy-agent/gatekeeper/test/export/fake-subscriber/main . USER 65532:65532 diff --git a/test/pubsub/fake-subscriber/main.go b/test/export/fake-subscriber/main.go similarity index 100% rename from test/pubsub/fake-subscriber/main.go rename to test/export/fake-subscriber/main.go diff --git a/test/pubsub/fake-subscriber/manifest/subscriber.yaml b/test/export/fake-subscriber/manifest/subscriber.yaml similarity index 100% rename from test/pubsub/fake-subscriber/manifest/subscriber.yaml rename to test/export/fake-subscriber/manifest/subscriber.yaml diff --git a/test/pubsub/k8srequiredlabels_ct.yaml b/test/export/k8srequiredlabels_ct.yaml similarity index 100% rename from test/pubsub/k8srequiredlabels_ct.yaml rename to test/export/k8srequiredlabels_ct.yaml diff --git a/test/pubsub/nginx_deployment.yaml b/test/export/nginx_deployment.yaml similarity index 100% rename from test/pubsub/nginx_deployment.yaml rename to test/export/nginx_deployment.yaml diff --git a/test/pubsub/pod_must_have_test.yaml b/test/export/pod_must_have_test.yaml similarity index 100% rename from test/pubsub/pod_must_have_test.yaml rename to test/export/pod_must_have_test.yaml diff --git a/test/pubsub/publish-components.yaml b/test/export/publish-components.yaml similarity index 100% rename from test/pubsub/publish-components.yaml rename to test/export/publish-components.yaml diff --git a/website/docs/export.md b/website/docs/export.md index 5af87327c8a..6e4d8fc2fb6 100644 --- a/website/docs/export.md +++ b/website/docs/export.md @@ -19,9 +19,9 @@ Install prerequisites such as a pubsub tool, a message broker etc. ### Setting up audit to export violations -In the audit deployment, set the `--enable-pub-sub` flag to `true` to publish audit violations. Additionally, use `--audit-connection` (defaults to `audit-connection`) and `--audit-channel`(defaults to `audit-channel`) flags to allow audit to publish violations using desired connection onto desired channel. `--audit-connection` must be set to the name of the connection config, and `--audit-channel` must be set to name of the channel where violations should get published. +In the audit deployment, set the `--export-violations` flag to `true` to export audit violations. Additionally, use `--audit-connection` (defaults to `audit-connection`) and `--audit-channel`(defaults to `audit-channel`) flags to allow audit to export violations using desired connection onto desired channel. `--audit-connection` must be set to the name of the connection config, and `--audit-channel` must be set to name of the channel where violations should get published. -A ConfigMap that contains `driver` and `config` fields in `data` is required to establish connection for sending violations over the channel. Following is an example ConfigMap to establish a connection that uses Dapr to publish messages: +A ConfigMap that contains `driver` and `config` fields in `data` is required to establish connection for sending violations over the channel. Following is an example ConfigMap to establish a connection that uses Dapr to export messages: ```yaml apiVersion: v1 @@ -128,7 +128,7 @@ Dapr: https://dapr.io/ ``` > [!IMPORTANT] - > Please make sure `fake-subscriber` image is built and available in your cluster. Dockerfile to build image for `fake-subscriber` is under [gatekeeper/test/fake-subscriber](https://github.com/open-policy-agent/gatekeeper/tree/master/test/pubsub/fake-subscriber). + > Please make sure `fake-subscriber` image is built and available in your cluster. Dockerfile to build image for `fake-subscriber` is under [gatekeeper/test/fake-subscriber](https://github.com/open-policy-agent/gatekeeper/tree/master/test/export/fake-subscriber). #### Configure Gatekeeper with Export enabled @@ -156,13 +156,13 @@ Dapr: https://dapr.io/ EOF ``` -2. To upgrade or install Gatekeeper with `--enable-pub-sub` set to `true`, `--audit-connection` set to `audit-connection`, `--audit-channel` set to `audit-channel` on audit pod. +2. To upgrade or install Gatekeeper with `--export-violations` set to `true`, `--audit-connection` set to `audit-connection`, `--audit-channel` set to `audit-channel` on audit pod. ```shell # auditPodAnnotations is used to add annotations required by Dapr to inject sidecar to audit pod echo 'auditPodAnnotations: {dapr.io/enabled: "true", dapr.io/app-id: "audit", dapr.io/metrics-port: "9999", dapr.io/sidecar-seccomp-profile-type: "RuntimeDefault"}' > /tmp/annotations.yaml helm upgrade --install gatekeeper gatekeeper/gatekeeper --namespace gatekeeper-system \ - --set audit.enablePubsub=true \ + --set audit.exportViolations=true \ --set audit.connection=audit-connection \ --set audit.channel=audit-channel \ --values /tmp/annotations.yaml @@ -188,7 +188,7 @@ Dapr: https://dapr.io/ EOF ``` - **Note:** Name of the connection configMap must match the value of `--audit-connection` for it to be used by audit to publish violation. At the moment, only one connection config can exists for audit. + **Note:** Name of the connection configMap must match the value of `--audit-connection` for it to be used by audit to export violation. At the moment, only one connection config can exists for audit. 4. Create the constraint templates and constraints, and make sure audit ran by checking constraints. If constraint status is updated with information such as `auditTimeStamp` or `totalViolations`, then audit has ran at least once. Additionally, populated `TOTAL-VIOLATIONS` field for all constraints while listing constraints also indicates that audit has ran at least once. @@ -203,12 +203,12 @@ Dapr: https://dapr.io/ ```log kubectl logs -l app=sub -c go-sub -n fake-subscriber 2023/07/18 20:16:41 Listening... - 2023/07/18 20:37:20 main.PubsubMsg{ID:"2023-07-18T20:37:19Z", Details:map[string]interface {}{"missing_labels":[]interface {}{"test"}}, EventType:"violation_audited", Group:"constraints.gatekeeper.sh", Version:"v1beta1", Kind:"K8sRequiredLabels", Name:"pod-must-have-test", Namespace:"", Message:"you must provide labels: {\"test\"}", EnforcementAction:"deny", ConstraintAnnotations:map[string]string(nil), ResourceGroup:"", ResourceAPIVersion:"v1", ResourceKind:"Pod", ResourceNamespace:"nginx", ResourceName:"nginx-deployment-58899467f5-j85bs", ResourceLabels:map[string]string{"app":"nginx", "owner":"admin", "pod-template-hash":"58899467f5"}} + 2023/07/18 20:37:20 main.ExportMsg{ID:"2023-07-18T20:37:19Z", Details:map[string]interface {}{"missing_labels":[]interface {}{"test"}}, EventType:"violation_audited", Group:"constraints.gatekeeper.sh", Version:"v1beta1", Kind:"K8sRequiredLabels", Name:"pod-must-have-test", Namespace:"", Message:"you must provide labels: {\"test\"}", EnforcementAction:"deny", ConstraintAnnotations:map[string]string(nil), ResourceGroup:"", ResourceAPIVersion:"v1", ResourceKind:"Pod", ResourceNamespace:"nginx", ResourceName:"nginx-deployment-58899467f5-j85bs", ResourceLabels:map[string]string{"app":"nginx", "owner":"admin", "pod-template-hash":"58899467f5"}} ``` ### Violations -The audit pod publishes violations in following format: +The audit pod exports violations in following format: ```json {