diff --git a/e2e/fixtures/E2E_CLI_005_PAYLOAD.json b/e2e/fixtures/E2E_CLI_005_PAYLOAD.json index 6af882b08..7b29648f8 100644 --- a/e2e/fixtures/E2E_CLI_005_PAYLOAD.json +++ b/e2e/fixtures/E2E_CLI_005_PAYLOAD.json @@ -1 +1 @@ -{"document":[{"id":"0","file":"file","resource":{"aws_redshift_cluster":{"default":{"node_type":"dc1.large","cluster_type":"single-node","cluster_identifier":"tf-redshift-cluster","database_name":"mydb","master_username":"foo","master_password":"Mustbe8characters"},"default1":{"master_password":"Mustbe8characters","node_type":"dc1.large","cluster_type":"single-node","publicly_accessible":true,"cluster_identifier":"tf-redshift-cluster","database_name":"mydb","master_username":"foo"}}}}]} \ No newline at end of file +{"document":[{"id":"0","file":"file","resource":{"aws_redshift_cluster":{"default":{"node_type":"dc1.large","cluster_type":"single-node","cluster_identifier":"tf-redshift-cluster","database_name":"mydb","master_username":"foo","master_password":"Mustbe8characters"},"default1":{"master_password":"Mustbe8characters","node_type":"dc1.large","cluster_type":"single-node","publicly_accessible":true,"cluster_identifier":"tf-redshift-cluster","database_name":"mydb","master_username":"foo"}}}}]} diff --git a/test/fixtures/multi_kustomize/k8s-a/cm.yaml b/test/fixtures/multi_kustomize/k8s-a/cm.yaml new file mode 100644 index 000000000..0dbe44793 --- /dev/null +++ b/test/fixtures/multi_kustomize/k8s-a/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-a +data: + k: v diff --git a/test/fixtures/multi_kustomize/k8s-a/kustomization.yaml b/test/fixtures/multi_kustomize/k8s-a/kustomization.yaml new file mode 100644 index 000000000..77c753a51 --- /dev/null +++ b/test/fixtures/multi_kustomize/k8s-a/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - cm.yaml diff --git a/test/fixtures/multi_kustomize/k8s-b/cm.yaml b/test/fixtures/multi_kustomize/k8s-b/cm.yaml new file mode 100644 index 000000000..d4e6c0952 --- /dev/null +++ b/test/fixtures/multi_kustomize/k8s-b/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-b +data: + k: v diff --git a/test/fixtures/multi_kustomize/k8s-b/kustomization.yaml b/test/fixtures/multi_kustomize/k8s-b/kustomization.yaml new file mode 100644 index 000000000..77c753a51 --- /dev/null +++ b/test/fixtures/multi_kustomize/k8s-b/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - cm.yaml diff --git a/test/fixtures/scan_scenarios/01-simple/deployment.yaml b/test/fixtures/scan_scenarios/01-simple/deployment.yaml new file mode 100644 index 000000000..2bb675516 --- /dev/null +++ b/test/fixtures/scan_scenarios/01-simple/deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: web + image: nginx:latest + securityContext: + privileged: true + runAsUser: 0 + allowPrivilegeEscalation: true diff --git a/test/fixtures/scan_scenarios/01-simple/kustomization.yaml b/test/fixtures/scan_scenarios/01-simple/kustomization.yaml new file mode 100644 index 000000000..42835f535 --- /dev/null +++ b/test/fixtures/scan_scenarios/01-simple/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml diff --git a/test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml b/test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml new file mode 100644 index 000000000..8ec39f219 --- /dev/null +++ b/test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api +spec: + replicas: 1 + selector: + matchLabels: + app: api + template: + metadata: + labels: + app: api + spec: + hostNetwork: true + containers: + - name: api + image: quay.io/example/api:1.0 + securityContext: + runAsNonRoot: false diff --git a/test/fixtures/scan_scenarios/02-overlay/base/kustomization.yaml b/test/fixtures/scan_scenarios/02-overlay/base/kustomization.yaml new file mode 100644 index 000000000..42835f535 --- /dev/null +++ b/test/fixtures/scan_scenarios/02-overlay/base/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml diff --git a/test/fixtures/scan_scenarios/02-overlay/overlay/kustomization.yaml b/test/fixtures/scan_scenarios/02-overlay/overlay/kustomization.yaml new file mode 100644 index 000000000..f6b2f2735 --- /dev/null +++ b/test/fixtures/scan_scenarios/02-overlay/overlay/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: production +resources: +- ../base +patches: +- path: replicas_patch.yaml diff --git a/test/fixtures/scan_scenarios/02-overlay/overlay/replicas_patch.yaml b/test/fixtures/scan_scenarios/02-overlay/overlay/replicas_patch.yaml new file mode 100644 index 000000000..563ff3c05 --- /dev/null +++ b/test/fixtures/scan_scenarios/02-overlay/overlay/replicas_patch.yaml @@ -0,0 +1,6 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api +spec: + replicas: 5 diff --git a/test/fixtures/scan_scenarios/03-generator/kustomization.yaml b/test/fixtures/scan_scenarios/03-generator/kustomization.yaml new file mode 100644 index 000000000..9d55d33f0 --- /dev/null +++ b/test/fixtures/scan_scenarios/03-generator/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- pod.yaml +configMapGenerator: +- name: app-config + literals: + - DB_HOST=postgres.internal + - DB_PASSWORD=SuperSecret123! +secretGenerator: +- name: api-token + literals: + - token=abcdef0123456789 diff --git a/test/fixtures/scan_scenarios/03-generator/pod.yaml b/test/fixtures/scan_scenarios/03-generator/pod.yaml new file mode 100644 index 000000000..1181a69fb --- /dev/null +++ b/test/fixtures/scan_scenarios/03-generator/pod.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: consumer +spec: + containers: + - name: consumer + image: busybox + envFrom: + - configMapRef: + name: app-config + - secretRef: + name: api-token diff --git a/test/fixtures/scan_scenarios/04-nested/base/kustomization.yaml b/test/fixtures/scan_scenarios/04-nested/base/kustomization.yaml new file mode 100644 index 000000000..f0f2a16db --- /dev/null +++ b/test/fixtures/scan_scenarios/04-nested/base/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- service.yaml diff --git a/test/fixtures/scan_scenarios/04-nested/base/service.yaml b/test/fixtures/scan_scenarios/04-nested/base/service.yaml new file mode 100644 index 000000000..5590847c4 --- /dev/null +++ b/test/fixtures/scan_scenarios/04-nested/base/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: shared +spec: + type: LoadBalancer + selector: + app: shared + ports: + - port: 80 + targetPort: 8080 diff --git a/test/fixtures/scan_scenarios/04-nested/prod/kustomization.yaml b/test/fixtures/scan_scenarios/04-nested/prod/kustomization.yaml new file mode 100644 index 000000000..37f3d961c --- /dev/null +++ b/test/fixtures/scan_scenarios/04-nested/prod/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: production +resources: +- ../staging +patches: +- path: loadbalancer_patch.yaml + target: + kind: Service + name: shared diff --git a/test/fixtures/scan_scenarios/04-nested/prod/loadbalancer_patch.yaml b/test/fixtures/scan_scenarios/04-nested/prod/loadbalancer_patch.yaml new file mode 100644 index 000000000..b3d6ae39a --- /dev/null +++ b/test/fixtures/scan_scenarios/04-nested/prod/loadbalancer_patch.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Service +metadata: + name: shared +spec: + externalTrafficPolicy: Cluster diff --git a/test/fixtures/scan_scenarios/04-nested/staging/kustomization.yaml b/test/fixtures/scan_scenarios/04-nested/staging/kustomization.yaml new file mode 100644 index 000000000..9a92a2705 --- /dev/null +++ b/test/fixtures/scan_scenarios/04-nested/staging/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: staging +resources: +- ../base +commonLabels: + env: staging diff --git a/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/Chart.yaml b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/Chart.yaml new file mode 100644 index 000000000..bc4a8a395 --- /dev/null +++ b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: demo +description: Local chart for scanner integration test +type: application +version: 0.1.0 +appVersion: "1.0" diff --git a/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/templates/deployment.yaml b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/templates/deployment.yaml new file mode 100644 index 000000000..6d0806d51 --- /dev/null +++ b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/templates/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-demo +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: demo + template: + metadata: + labels: + app: demo + spec: + containers: + - name: app + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + securityContext: + privileged: {{ .Values.privileged }} + runAsUser: 0 diff --git a/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/values.yaml b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/values.yaml new file mode 100644 index 000000000..9a8c98c06 --- /dev/null +++ b/test/fixtures/scan_scenarios/05-helm-inflation/charts/demo/values.yaml @@ -0,0 +1,5 @@ +image: + repository: nginx + tag: latest +replicas: 2 +privileged: false diff --git a/test/fixtures/scan_scenarios/05-helm-inflation/kustomization.yaml b/test/fixtures/scan_scenarios/05-helm-inflation/kustomization.yaml new file mode 100644 index 000000000..eb4c29a20 --- /dev/null +++ b/test/fixtures/scan_scenarios/05-helm-inflation/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +helmCharts: +- name: demo + releaseName: demo + namespace: demo + valuesInline: + privileged: true + replicas: 3 diff --git a/test/fixtures/scan_scenarios/06-remote-blocked/kustomization.yaml b/test/fixtures/scan_scenarios/06-remote-blocked/kustomization.yaml new file mode 100644 index 000000000..3054ecbb1 --- /dev/null +++ b/test/fixtures/scan_scenarios/06-remote-blocked/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- https://github.com/kubernetes-sigs/kustomize/examples/multibases/dev diff --git a/test/fixtures/scan_scenarios/07-broken/deployment.yaml b/test/fixtures/scan_scenarios/07-broken/deployment.yaml new file mode 100644 index 000000000..28c84f283 --- /dev/null +++ b/test/fixtures/scan_scenarios/07-broken/deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: broken +spec: + replicas: 1 + selector: + matchLabels: + app: broken + template: + metadata: + labels: + app: broken + spec: + containers: + - name: broken + image: alpine diff --git a/test/fixtures/scan_scenarios/07-broken/kustomization.yaml b/test/fixtures/scan_scenarios/07-broken/kustomization.yaml new file mode 100644 index 000000000..cc4b9c6ea --- /dev/null +++ b/test/fixtures/scan_scenarios/07-broken/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +patches: +- path: nonexistent_patch.yaml + target: + kind: Deployment + name: does-not-exist diff --git a/test/fixtures/scan_scenarios/08-pure-helm/Chart.yaml b/test/fixtures/scan_scenarios/08-pure-helm/Chart.yaml new file mode 100644 index 000000000..fcb94fc48 --- /dev/null +++ b/test/fixtures/scan_scenarios/08-pure-helm/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: standalone +description: Standalone Helm chart (no Kustomize wrapper) +type: application +version: 0.1.0 +appVersion: "1.0" diff --git a/test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml b/test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml new file mode 100644 index 000000000..e5422c1e0 --- /dev/null +++ b/test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: standalone +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app: standalone + template: + metadata: + labels: + app: standalone + spec: + hostPID: true + containers: + - name: standalone + image: {{ .Values.image }} + securityContext: + privileged: true diff --git a/test/fixtures/scan_scenarios/08-pure-helm/values.yaml b/test/fixtures/scan_scenarios/08-pure-helm/values.yaml new file mode 100644 index 000000000..3d33e26e4 --- /dev/null +++ b/test/fixtures/scan_scenarios/08-pure-helm/values.yaml @@ -0,0 +1,2 @@ +image: nginx:1.25 +replicas: 1 diff --git a/test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml b/test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml new file mode 100644 index 000000000..b609e116f --- /dev/null +++ b/test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: plain-root + namespace: default +spec: + hostNetwork: true + hostPID: true + containers: + - name: plain + image: busybox:latest + securityContext: + privileged: true + runAsUser: 0 + allowPrivilegeEscalation: true diff --git a/test/fixtures/scan_scenarios/10-multi-root/team-a/deployment.yaml b/test/fixtures/scan_scenarios/10-multi-root/team-a/deployment.yaml new file mode 100644 index 000000000..21e93a263 --- /dev/null +++ b/test/fixtures/scan_scenarios/10-multi-root/team-a/deployment.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: worker +spec: + replicas: 1 + selector: + matchLabels: + app: worker + template: + metadata: + labels: + app: worker + spec: + containers: + - name: worker + image: alpine:latest + securityContext: + readOnlyRootFilesystem: false + runAsNonRoot: false diff --git a/test/fixtures/scan_scenarios/10-multi-root/team-a/kustomization.yaml b/test/fixtures/scan_scenarios/10-multi-root/team-a/kustomization.yaml new file mode 100644 index 000000000..53d7d4bf2 --- /dev/null +++ b/test/fixtures/scan_scenarios/10-multi-root/team-a/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +namespace: team-a diff --git a/test/fixtures/scan_scenarios/10-multi-root/team-b/kustomization.yaml b/test/fixtures/scan_scenarios/10-multi-root/team-b/kustomization.yaml new file mode 100644 index 000000000..2fd3cb18f --- /dev/null +++ b/test/fixtures/scan_scenarios/10-multi-root/team-b/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- statefulset.yaml +namespace: team-b diff --git a/test/fixtures/scan_scenarios/10-multi-root/team-b/statefulset.yaml b/test/fixtures/scan_scenarios/10-multi-root/team-b/statefulset.yaml new file mode 100644 index 000000000..c90fc3d97 --- /dev/null +++ b/test/fixtures/scan_scenarios/10-multi-root/team-b/statefulset.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: cache +spec: + serviceName: cache + replicas: 3 + selector: + matchLabels: + app: cache + template: + metadata: + labels: + app: cache + spec: + containers: + - name: cache + image: redis:latest + securityContext: + privileged: true diff --git a/test/fixtures/scan_scenarios/expected/01-simple.json b/test/fixtures/scan_scenarios/expected/01-simple.json new file mode 100644 index 000000000..d122f1aa8 --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/01-simple.json @@ -0,0 +1,336 @@ +{ + "rules": [ + { + "id": "Container is privileged", + "name": "Container is privileged", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/container_is_privileged", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dd29336b-fe57-445b-a26e-e6aa867ae609" + ] + }, + { + "id": "Container running as root", + "name": "Container running as root", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_running_as_root", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:cf34805e-3872-4c08-bf92-6ff7bb0cfadb" + ] + }, + { + "id": "Container with low UID", + "name": "Container with low UID", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_run_with_low_uid", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:02323c00-cdc3-4fdc-a310-4f2b3e7a1660" + ] + }, + { + "id": "Memory limits not defined", + "name": "Memory limits not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_limits_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:b14d1bc4-a208-45db-92f0-e21f8e2588e9" + ] + }, + { + "id": "Memory requests not defined", + "name": "Memory requests not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_requests_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:229588ef-8fde-40c8-8756-f4f2b5825ded" + ] + }, + { + "id": "NET_RAW capabilities not dropped", + "name": "NET_RAW capabilities not dropped", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/net_raw_capabilities_not_being_dropped", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dbbc6705-d541-43b0-b166-dd4be8208b54" + ] + }, + { + "id": "Privilege escalation allowed", + "name": "Privilege escalation allowed", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/privilege_escalation_allowed", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:5572cc5e-1e4c-4113-92a6-7a8a3bd25e6d" + ] + }, + { + "id": "Readiness probe is not configured", + "name": "Readiness probe is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/readiness_probe_is_not_configured", + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:a659f3b5-9bf0-438a-bd9a-7d3a6427f1e3" + ] + }, + { + "id": "Seccomp profile is not configured", + "name": "Seccomp profile is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/seccomp_profile_is_not_configured", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:f377b83e-bd07-4f48-a591-60c82b14a78b" + ] + }, + { + "id": "Service account token auto-mount not disabled", + "name": "Service account token auto-mount not disabled", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_token_automount_not_disabled", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:48471392-d4d0-47c0-b135-cdec95eb3eef" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Container is privileged", + "level": "warning", + "message": "Container is privileged", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Container running as root", + "level": "note", + "message": "Container running as root", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Container with low UID", + "level": "note", + "message": "Container with low UID", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory limits not defined", + "level": "note", + "message": "Memory limits not defined", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory requests not defined", + "level": "note", + "message": "Memory requests not defined", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "NET_RAW capabilities not dropped", + "level": "note", + "message": "NET_RAW capabilities not dropped", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Privilege escalation allowed", + "level": "warning", + "message": "Privilege escalation allowed", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Readiness probe is not configured", + "level": "note", + "message": "Readiness probe is not configured", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Seccomp profile is not configured", + "level": "note", + "message": "Seccomp profile is not configured", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Service account token auto-mount not disabled", + "level": "note", + "message": "Service account token auto-mount not disabled", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/01-simple/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:web", + "IAC_RESOURCE_TYPE:Deployment" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/02-overlay.json b/test/fixtures/scan_scenarios/expected/02-overlay.json new file mode 100644 index 000000000..6c60d1390 --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/02-overlay.json @@ -0,0 +1,336 @@ +{ + "rules": [ + { + "id": "Container running as root", + "name": "Container running as root", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_running_as_root", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:cf34805e-3872-4c08-bf92-6ff7bb0cfadb" + ] + }, + { + "id": "Container with low UID", + "name": "Container with low UID", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_run_with_low_uid", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:02323c00-cdc3-4fdc-a310-4f2b3e7a1660" + ] + }, + { + "id": "Memory limits not defined", + "name": "Memory limits not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_limits_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:b14d1bc4-a208-45db-92f0-e21f8e2588e9" + ] + }, + { + "id": "Memory requests not defined", + "name": "Memory requests not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_requests_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:229588ef-8fde-40c8-8756-f4f2b5825ded" + ] + }, + { + "id": "NET_RAW capabilities not dropped", + "name": "NET_RAW capabilities not dropped", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/net_raw_capabilities_not_being_dropped", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dbbc6705-d541-43b0-b166-dd4be8208b54" + ] + }, + { + "id": "Privilege escalation allowed", + "name": "Privilege escalation allowed", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/privilege_escalation_allowed", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:5572cc5e-1e4c-4113-92a6-7a8a3bd25e6d" + ] + }, + { + "id": "Readiness probe is not configured", + "name": "Readiness probe is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/readiness_probe_is_not_configured", + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:a659f3b5-9bf0-438a-bd9a-7d3a6427f1e3" + ] + }, + { + "id": "Seccomp profile is not configured", + "name": "Seccomp profile is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/seccomp_profile_is_not_configured", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:f377b83e-bd07-4f48-a591-60c82b14a78b" + ] + }, + { + "id": "Service account token auto-mount not disabled", + "name": "Service account token auto-mount not disabled", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_token_automount_not_disabled", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:48471392-d4d0-47c0-b135-cdec95eb3eef" + ] + }, + { + "id": "Shared host network namespace", + "name": "Shared host network namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/shared_host_network_namespace", + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:6b6bdfb3-c3ae-44cb-88e4-7405c1ba2c8a" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Container running as root", + "level": "note", + "message": "Container running as root", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Container with low UID", + "level": "note", + "message": "Container with low UID", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory limits not defined", + "level": "note", + "message": "Memory limits not defined", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory requests not defined", + "level": "note", + "message": "Memory requests not defined", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "NET_RAW capabilities not dropped", + "level": "note", + "message": "NET_RAW capabilities not dropped", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Privilege escalation allowed", + "level": "warning", + "message": "Privilege escalation allowed", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Readiness probe is not configured", + "level": "note", + "message": "Readiness probe is not configured", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Seccomp profile is not configured", + "level": "note", + "message": "Seccomp profile is not configured", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Service account token auto-mount not disabled", + "level": "note", + "message": "Service account token auto-mount not disabled", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Shared host network namespace", + "level": "note", + "message": "Shared host network namespace", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/02-overlay/base/deployment.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 2, + "endColumn": 11, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:api", + "IAC_RESOURCE_TYPE:Deployment" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/03-generator.json b/test/fixtures/scan_scenarios/expected/03-generator.json new file mode 100644 index 000000000..b1f9774de --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/03-generator.json @@ -0,0 +1,276 @@ +{ + "rules": [ + { + "id": "Container running as root", + "name": "Container running as root", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_running_as_root", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:cf34805e-3872-4c08-bf92-6ff7bb0cfadb" + ] + }, + { + "id": "Container with low UID", + "name": "Container with low UID", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_run_with_low_uid", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:02323c00-cdc3-4fdc-a310-4f2b3e7a1660" + ] + }, + { + "id": "Memory limits not defined", + "name": "Memory limits not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_limits_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:b14d1bc4-a208-45db-92f0-e21f8e2588e9" + ] + }, + { + "id": "Memory requests not defined", + "name": "Memory requests not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_requests_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:229588ef-8fde-40c8-8756-f4f2b5825ded" + ] + }, + { + "id": "Readiness probe is not configured", + "name": "Readiness probe is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/readiness_probe_is_not_configured", + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:a659f3b5-9bf0-438a-bd9a-7d3a6427f1e3" + ] + }, + { + "id": "Seccomp profile is not configured", + "name": "Seccomp profile is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/seccomp_profile_is_not_configured", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:f377b83e-bd07-4f48-a591-60c82b14a78b" + ] + }, + { + "id": "Service account name undefined or empty", + "name": "Service account name undefined or empty", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_name_undefined_or_empty", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:591ade62-d6b0-4580-b1ae-209f80ba1cd9" + ] + }, + { + "id": "Service account token auto-mount not disabled", + "name": "Service account token auto-mount not disabled", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_token_automount_not_disabled", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:48471392-d4d0-47c0-b135-cdec95eb3eef" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Container running as root", + "level": "note", + "message": "Container running as root", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Container with low UID", + "level": "note", + "message": "Container with low UID", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Memory limits not defined", + "level": "note", + "message": "Memory limits not defined", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Memory requests not defined", + "level": "note", + "message": "Memory requests not defined", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Readiness probe is not configured", + "level": "note", + "message": "Readiness probe is not configured", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Seccomp profile is not configured", + "level": "note", + "message": "Seccomp profile is not configured", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Service account name undefined or empty", + "level": "note", + "message": "Service account name undefined or empty", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Service account token auto-mount not disabled", + "level": "note", + "message": "Service account token auto-mount not disabled", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/03-generator/pod.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 2, + "endColumn": 16, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:consumer", + "IAC_RESOURCE_TYPE:Pod" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/04-nested.json b/test/fixtures/scan_scenarios/expected/04-nested.json new file mode 100644 index 000000000..99cdadcf1 --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/04-nested.json @@ -0,0 +1,66 @@ +{ + "rules": [ + { + "id": "Service with external load balancer", + "name": "Service with external load balancer", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_with_external_load_balancer", + "tags": [ + "CWE:552", + "DATADOG_CATEGORY:Networking and Firewall", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:26763a1c-5dda-4772-b507-5fca7fb5f165" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Service with external load balancer", + "level": "note", + "message": "Service with external load balancer", + "artifactUri": "test/fixtures/scan_scenarios/04-nested/base/service.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "CWE:552", + "DATADOG_CATEGORY:Networking and Firewall", + "IAC_RESOURCE_NAME:shared", + "IAC_RESOURCE_TYPE:Service" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/04-nested/base/service.yaml", + "startLine": 4, + "endLine": 4, + "startColumn": 2, + "endColumn": 14, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:shared", + "IAC_RESOURCE_TYPE:Service" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/05-helm-inflation.json b/test/fixtures/scan_scenarios/expected/05-helm-inflation.json new file mode 100644 index 000000000..049f3351a --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/05-helm-inflation.json @@ -0,0 +1,4 @@ +{ + "rules": [], + "results": [] +} diff --git a/test/fixtures/scan_scenarios/expected/06-remote-blocked.json b/test/fixtures/scan_scenarios/expected/06-remote-blocked.json new file mode 100644 index 000000000..d1f174a53 --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/06-remote-blocked.json @@ -0,0 +1,34 @@ +{ + "rules": [ + { + "id": "Kustomize remote reference blocked", + "name": "Kustomize remote reference blocked", + "level": "none", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/", + "tags": [ + "DATADOG_CATEGORY:Resolver", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:kustomize-remote-disallowed" + ] + } + ], + "results": [ + { + "ruleId": "Kustomize remote reference blocked", + "level": "none", + "message": "Kustomize remote reference blocked", + "artifactUri": "test/fixtures/scan_scenarios/06-remote-blocked/kustomization.yaml", + "startLine": 1, + "endLine": 1, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "DATADOG_CATEGORY:Resolver", + "IAC_RESOURCE_NAME:", + "IAC_RESOURCE_TYPE:" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/07-broken.json b/test/fixtures/scan_scenarios/expected/07-broken.json new file mode 100644 index 000000000..abc78cf6b --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/07-broken.json @@ -0,0 +1,34 @@ +{ + "rules": [ + { + "id": "Kustomize render failed", + "name": "Kustomize render failed", + "level": "none", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/", + "tags": [ + "DATADOG_CATEGORY:Resolver", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:kustomize-render-failed" + ] + } + ], + "results": [ + { + "ruleId": "Kustomize render failed", + "level": "none", + "message": "Kustomize render failed", + "artifactUri": "test/fixtures/scan_scenarios/07-broken", + "startLine": 1, + "endLine": 1, + "startColumn": 1, + "endColumn": 1, + "tags": [ + "DATADOG_CATEGORY:Resolver", + "IAC_RESOURCE_NAME:", + "IAC_RESOURCE_TYPE:" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/08-pure-helm.json b/test/fixtures/scan_scenarios/expected/08-pure-helm.json new file mode 100644 index 000000000..6f8afc5ab --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/08-pure-helm.json @@ -0,0 +1,366 @@ +{ + "rules": [ + { + "id": "Container is privileged", + "name": "Container is privileged", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/container_is_privileged", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dd29336b-fe57-445b-a26e-e6aa867ae609" + ] + }, + { + "id": "Container running as root", + "name": "Container running as root", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_running_as_root", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:cf34805e-3872-4c08-bf92-6ff7bb0cfadb" + ] + }, + { + "id": "Container with low UID", + "name": "Container with low UID", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_run_with_low_uid", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:02323c00-cdc3-4fdc-a310-4f2b3e7a1660" + ] + }, + { + "id": "Memory limits not defined", + "name": "Memory limits not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_limits_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:b14d1bc4-a208-45db-92f0-e21f8e2588e9" + ] + }, + { + "id": "Memory requests not defined", + "name": "Memory requests not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_requests_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:229588ef-8fde-40c8-8756-f4f2b5825ded" + ] + }, + { + "id": "NET_RAW capabilities not dropped", + "name": "NET_RAW capabilities not dropped", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/net_raw_capabilities_not_being_dropped", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dbbc6705-d541-43b0-b166-dd4be8208b54" + ] + }, + { + "id": "Privilege escalation allowed", + "name": "Privilege escalation allowed", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/privilege_escalation_allowed", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:5572cc5e-1e4c-4113-92a6-7a8a3bd25e6d" + ] + }, + { + "id": "Readiness probe is not configured", + "name": "Readiness probe is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/readiness_probe_is_not_configured", + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:a659f3b5-9bf0-438a-bd9a-7d3a6427f1e3" + ] + }, + { + "id": "Seccomp profile is not configured", + "name": "Seccomp profile is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/seccomp_profile_is_not_configured", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:f377b83e-bd07-4f48-a591-60c82b14a78b" + ] + }, + { + "id": "Service account token auto-mount not disabled", + "name": "Service account token auto-mount not disabled", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_token_automount_not_disabled", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:48471392-d4d0-47c0-b135-cdec95eb3eef" + ] + }, + { + "id": "Shared host PID namespace", + "name": "Shared host PID namespace", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/shared_host_pid_namespace", + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:302736f4-b16c-41b8-befe-c0baffa0bd9d" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Container is privileged", + "level": "warning", + "message": "Container is privileged", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 21, + "endLine": 21, + "startColumn": 1, + "endColumn": 26, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Container running as root", + "level": "note", + "message": "Container running as root", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 18, + "endLine": 18, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Container with low UID", + "level": "note", + "message": "Container with low UID", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 20, + "endLine": 20, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory limits not defined", + "level": "note", + "message": "Memory limits not defined", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 18, + "endLine": 18, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Memory requests not defined", + "level": "note", + "message": "Memory requests not defined", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 18, + "endLine": 18, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "NET_RAW capabilities not dropped", + "level": "note", + "message": "NET_RAW capabilities not dropped", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 18, + "endLine": 18, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Privilege escalation allowed", + "level": "warning", + "message": "Privilege escalation allowed", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 20, + "endLine": 20, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Readiness probe is not configured", + "level": "note", + "message": "Readiness probe is not configured", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 18, + "endLine": 18, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Seccomp profile is not configured", + "level": "note", + "message": "Seccomp profile is not configured", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 20, + "endLine": 20, + "startColumn": 1, + "endColumn": 24, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Service account token auto-mount not disabled", + "level": "note", + "message": "Service account token auto-mount not disabled", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 15, + "endLine": 15, + "startColumn": 1, + "endColumn": 9, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Shared host PID namespace", + "level": "warning", + "message": "Shared host PID namespace", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 16, + "endLine": 16, + "startColumn": 1, + "endColumn": 19, + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/08-pure-helm/templates/deployment.yaml", + "startLine": 5, + "endLine": 5, + "startColumn": 1, + "endColumn": 18, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:standalone", + "IAC_RESOURCE_TYPE:Deployment" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/09-plain-k8s.json b/test/fixtures/scan_scenarios/expected/09-plain-k8s.json new file mode 100644 index 000000000..06a4a0056 --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/09-plain-k8s.json @@ -0,0 +1,426 @@ +{ + "rules": [ + { + "id": "Container is privileged", + "name": "Container is privileged", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/container_is_privileged", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dd29336b-fe57-445b-a26e-e6aa867ae609" + ] + }, + { + "id": "Container running as root", + "name": "Container running as root", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_running_as_root", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:cf34805e-3872-4c08-bf92-6ff7bb0cfadb" + ] + }, + { + "id": "Container with low UID", + "name": "Container with low UID", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/containers_run_with_low_uid", + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:02323c00-cdc3-4fdc-a310-4f2b3e7a1660" + ] + }, + { + "id": "Memory limits not defined", + "name": "Memory limits not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_limits_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:b14d1bc4-a208-45db-92f0-e21f8e2588e9" + ] + }, + { + "id": "Memory requests not defined", + "name": "Memory requests not defined", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/memory_requests_not_defined", + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:229588ef-8fde-40c8-8756-f4f2b5825ded" + ] + }, + { + "id": "NET_RAW capabilities not dropped", + "name": "NET_RAW capabilities not dropped", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/net_raw_capabilities_not_being_dropped", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:dbbc6705-d541-43b0-b166-dd4be8208b54" + ] + }, + { + "id": "Privilege escalation allowed", + "name": "Privilege escalation allowed", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/privilege_escalation_allowed", + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:5572cc5e-1e4c-4113-92a6-7a8a3bd25e6d" + ] + }, + { + "id": "Readiness probe is not configured", + "name": "Readiness probe is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/readiness_probe_is_not_configured", + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:a659f3b5-9bf0-438a-bd9a-7d3a6427f1e3" + ] + }, + { + "id": "Seccomp profile is not configured", + "name": "Seccomp profile is not configured", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/seccomp_profile_is_not_configured", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:f377b83e-bd07-4f48-a591-60c82b14a78b" + ] + }, + { + "id": "Service account name undefined or empty", + "name": "Service account name undefined or empty", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_name_undefined_or_empty", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:591ade62-d6b0-4580-b1ae-209f80ba1cd9" + ] + }, + { + "id": "Service account token auto-mount not disabled", + "name": "Service account token auto-mount not disabled", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/service_account_token_automount_not_disabled", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:48471392-d4d0-47c0-b135-cdec95eb3eef" + ] + }, + { + "id": "Shared host PID namespace", + "name": "Shared host PID namespace", + "level": "warning", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/shared_host_pid_namespace", + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:302736f4-b16c-41b8-befe-c0baffa0bd9d" + ] + }, + { + "id": "Shared host network namespace", + "name": "Shared host network namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/shared_host_network_namespace", + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Resource Management", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:6b6bdfb3-c3ae-44cb-88e4-7405c1ba2c8a" + ] + }, + { + "id": "Using unrecommended namespace", + "name": "Using unrecommended namespace", + "level": "note", + "helpUri": "https://docs.datadoghq.com/security/code_security/iac_security/iac_rules/k8s/using_unrecommended_namespace", + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "DATADOG_PLATFORM:Kubernetes", + "DATADOG_PROVIDER:", + "DATADOG_RULE_TYPE:IAC_SCANNING", + "KICS_RuleID:611ab018-c4aa-4ba2-b0f6-a448337509a6" + ] + } + ], + "results": [ + { + "ruleId": "Container is privileged", + "level": "warning", + "message": "Container is privileged", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 13, + "endLine": 13, + "startColumn": 6, + "endColumn": 22, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Container running as root", + "level": "note", + "message": "Container running as root", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 14, + "endLine": 14, + "startColumn": 6, + "endColumn": 18, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Container with low UID", + "level": "note", + "message": "Container with low UID", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 14, + "endLine": 14, + "startColumn": 6, + "endColumn": 18, + "tags": [ + "CWE:1188", + "DATADOG_CATEGORY:Best Practices", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Memory limits not defined", + "level": "note", + "message": "Memory limits not defined", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 10, + "endLine": 10, + "startColumn": 1, + "endColumn": 15, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Memory requests not defined", + "level": "note", + "message": "Memory requests not defined", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 10, + "endLine": 10, + "startColumn": 1, + "endColumn": 15, + "tags": [ + "CWE:400", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "NET_RAW capabilities not dropped", + "level": "note", + "message": "NET_RAW capabilities not dropped", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 10, + "endLine": 10, + "startColumn": 1, + "endColumn": 15, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Privilege escalation allowed", + "level": "warning", + "message": "Privilege escalation allowed", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 15, + "endLine": 15, + "startColumn": 6, + "endColumn": 36, + "tags": [ + "CWE:269", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Readiness probe is not configured", + "level": "note", + "message": "Readiness probe is not configured", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 10, + "endLine": 10, + "startColumn": 2, + "endColumn": 15, + "tags": [ + "CWE:754", + "DATADOG_CATEGORY:Availability", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Seccomp profile is not configured", + "level": "note", + "message": "Seccomp profile is not configured", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 12, + "endLine": 12, + "startColumn": 4, + "endColumn": 20, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Service account name undefined or empty", + "level": "note", + "message": "Service account name undefined or empty", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 6, + "endLine": 6, + "startColumn": 1, + "endColumn": 5, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Service account token auto-mount not disabled", + "level": "note", + "message": "Service account token auto-mount not disabled", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 6, + "endLine": 6, + "startColumn": 1, + "endColumn": 5, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Defaults", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Shared host PID namespace", + "level": "warning", + "message": "Shared host PID namespace", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 8, + "endLine": 8, + "startColumn": 2, + "endColumn": 15, + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Shared host network namespace", + "level": "note", + "message": "Shared host network namespace", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 7, + "endLine": 7, + "startColumn": 2, + "endColumn": 19, + "tags": [ + "CWE:200", + "DATADOG_CATEGORY:Resource Management", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + }, + { + "ruleId": "Using unrecommended namespace", + "level": "note", + "message": "Using unrecommended namespace", + "artifactUri": "test/fixtures/scan_scenarios/09-plain-k8s/pod.yaml", + "startLine": 5, + "endLine": 5, + "startColumn": 2, + "endColumn": 20, + "tags": [ + "CWE:665", + "DATADOG_CATEGORY:Insecure Configurations", + "IAC_RESOURCE_NAME:plain-root", + "IAC_RESOURCE_TYPE:Pod" + ] + } + ] +} diff --git a/test/fixtures/scan_scenarios/expected/10-multi-root.json b/test/fixtures/scan_scenarios/expected/10-multi-root.json new file mode 100644 index 000000000..049f3351a --- /dev/null +++ b/test/fixtures/scan_scenarios/expected/10-multi-root.json @@ -0,0 +1,4 @@ +{ + "rules": [], + "results": [] +} diff --git a/test/fixtures/test_kustomize/canonical/simple/cm.yaml b/test/fixtures/test_kustomize/canonical/simple/cm.yaml new file mode 100644 index 000000000..7ac3034d2 --- /dev/null +++ b/test/fixtures/test_kustomize/canonical/simple/cm.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: simple +data: + a: "1" diff --git a/test/fixtures/test_kustomize/canonical/simple/kustomization.yaml b/test/fixtures/test_kustomize/canonical/simple/kustomization.yaml new file mode 100644 index 000000000..9490e2f6d --- /dev/null +++ b/test/fixtures/test_kustomize/canonical/simple/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +buildMetadata: + - originAnnotations + - managedByLabel +kind: Kustomization +resources: + - cm.yaml diff --git a/test/fixtures/test_kustomize/regressions/namespace_overlay/base/deployment.yaml b/test/fixtures/test_kustomize/regressions/namespace_overlay/base/deployment.yaml new file mode 100644 index 000000000..77b23d625 --- /dev/null +++ b/test/fixtures/test_kustomize/regressions/namespace_overlay/base/deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sample-app +spec: + replicas: 1 + selector: + matchLabels: + app: sample + template: + metadata: + labels: + app: sample + spec: + containers: + - name: app + image: nginx:1.21 diff --git a/test/fixtures/test_kustomize/regressions/namespace_overlay/base/kustomization.yaml b/test/fixtures/test_kustomize/regressions/namespace_overlay/base/kustomization.yaml new file mode 100644 index 000000000..9c2d28b0c --- /dev/null +++ b/test/fixtures/test_kustomize/regressions/namespace_overlay/base/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - deployment.yaml diff --git a/test/fixtures/test_kustomize/regressions/namespace_overlay/overlay/kustomization.yaml b/test/fixtures/test_kustomize/regressions/namespace_overlay/overlay/kustomization.yaml new file mode 100644 index 000000000..2914e6bd1 --- /dev/null +++ b/test/fixtures/test_kustomize/regressions/namespace_overlay/overlay/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +buildMetadata: + - originAnnotations + - managedByLabel +kind: Kustomization +namespace: production +resources: + - ../base diff --git a/test/kustomize_e2e_test.go b/test/kustomize_e2e_test.go new file mode 100644 index 000000000..27df730e4 --- /dev/null +++ b/test/kustomize_e2e_test.go @@ -0,0 +1,37 @@ +package test + +import ( + "context" + "path/filepath" + "testing" + + "github.com/DataDog/datadog-iac-scanner/pkg/resolver/kustomize" + "github.com/stretchr/testify/require" +) + +// TestKustomizeEndToEnd verifies representative fixtures build and produce merged YAML. +func TestKustomizeEndToEnd(t *testing.T) { + if testing.Short() { + t.Skip() + } + ctx := context.Background() + repoRoot, err := filepath.Abs(filepath.Join("fixtures", "test_kustomize")) + require.NoError(t, err) + fixtures := []string{ + filepath.Join(repoRoot, "canonical", "simple"), + filepath.Join(repoRoot, "regressions", "namespace_overlay", "overlay"), + } + for _, root := range fixtures { + t.Run(filepath.Base(root), func(t *testing.T) { + r := kustomize.NewResolver(kustomize.Options{ + RepoRoot: repoRoot, + AllowHelmInflation: false, + HelmIncludeCRDs: true, + }) + out, err := r.Resolve(ctx, root) + require.NoError(t, err) + require.Empty(t, out.Diagnostics, "%+v", out.Diagnostics) + require.NotEmpty(t, out.File) + }) + } +} diff --git a/test/main_test.go b/test/main_test.go index a77a4e307..9c58bc803 100644 --- a/test/main_test.go +++ b/test/main_test.go @@ -25,6 +25,7 @@ import ( terraformParser "github.com/DataDog/datadog-iac-scanner/pkg/parser/terraform" cicdParser "github.com/DataDog/datadog-iac-scanner/pkg/parser/yaml/cicd" yamlParser "github.com/DataDog/datadog-iac-scanner/pkg/parser/yaml/default" + resolverkustomize "github.com/DataDog/datadog-iac-scanner/pkg/resolver/kustomize" "github.com/DataDog/datadog-iac-scanner/pkg/utils" "github.com/google/uuid" "github.com/rs/zerolog/log" @@ -96,6 +97,7 @@ const ( ) func TestMain(m *testing.M) { + resolverkustomize.MaybeRunAsKustomizeRenderHelper() os.Exit(m.Run()) } diff --git a/test/scan_scenarios_test.go b/test/scan_scenarios_test.go new file mode 100644 index 000000000..32f326d47 --- /dev/null +++ b/test/scan_scenarios_test.go @@ -0,0 +1,274 @@ +package test + +import ( + "context" + "encoding/json" + "flag" + "os" + "path/filepath" + "sort" + "testing" + + "github.com/DataDog/datadog-iac-scanner/internal/console" + "github.com/DataDog/datadog-iac-scanner/pkg/featureflags" + "github.com/DataDog/datadog-iac-scanner/pkg/model" + "github.com/DataDog/datadog-iac-scanner/pkg/scan" + "github.com/stretchr/testify/require" +) + +var updateScanScenarios = flag.Bool("update", false, "update scan scenario golden files") + +type sarifDocument struct { + Runs []sarifRun `json:"runs"` +} + +type sarifRun struct { + Tool sarifTool `json:"tool"` + Results []sarifResult `json:"results"` +} + +type sarifTool struct { + Driver sarifDriver `json:"driver"` +} + +type sarifDriver struct { + Rules []sarifRule `json:"rules"` +} + +type sarifRule struct { + ID string `json:"id"` + Name string `json:"name"` + HelpURI string `json:"helpUri"` + DefaultConfiguration sarifConfiguration `json:"defaultConfiguration"` + Properties map[string]interface{} `json:"properties"` +} + +type sarifConfiguration struct { + Level string `json:"level"` +} + +type sarifResult struct { + RuleID string `json:"ruleId"` + Level string `json:"level"` + Message sarifMessage `json:"message"` + Locations []sarifLocation `json:"locations"` + Properties map[string]interface{} `json:"properties"` +} + +type sarifMessage struct { + Text string `json:"text"` +} + +type sarifLocation struct { + PhysicalLocation sarifPhysicalLocation `json:"physicalLocation"` +} + +type sarifPhysicalLocation struct { + ArtifactLocation sarifArtifactLocation `json:"artifactLocation"` + Region sarifRegion `json:"region"` +} + +type sarifArtifactLocation struct { + ArtifactURI string `json:"uri"` +} + +type sarifRegion struct { + StartLine int `json:"startLine"` + EndLine int `json:"endLine"` + StartColumn int `json:"startColumn"` + EndColumn int `json:"endColumn"` +} + +type normalizedSARIF struct { + Rules []normalizedRule `json:"rules"` + Results []normalizedResult `json:"results"` +} + +type normalizedRule struct { + ID string `json:"id"` + Name string `json:"name"` + Level string `json:"level"` + HelpURI string `json:"helpUri,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +type normalizedResult struct { + RuleID string `json:"ruleId"` + Level string `json:"level"` + Message string `json:"message"` + ArtifactURI string `json:"artifactUri,omitempty"` + StartLine int `json:"startLine"` + EndLine int `json:"endLine"` + StartColumn int `json:"startColumn"` + EndColumn int `json:"endColumn"` + Tags []string `json:"tags,omitempty"` +} + +func TestScanScenarios(t *testing.T) { + repoRoot, err := filepath.Abs("..") + require.NoError(t, err) + + fixturesRoot := filepath.Join(repoRoot, "test", "fixtures", "scan_scenarios") + expectedRoot := filepath.Join(fixturesRoot, "expected") + entries, err := os.ReadDir(fixturesRoot) + require.NoError(t, err) + + var scenarios []string + for _, entry := range entries { + if !entry.IsDir() || entry.Name() == "expected" { + continue + } + scenarios = append(scenarios, entry.Name()) + } + sort.Strings(scenarios) + require.NotEmpty(t, scenarios) + + for _, scenario := range scenarios { + t.Run(scenario, func(t *testing.T) { + scenarioPath := filepath.Join(fixturesRoot, scenario) + normalized := runScenarioScan(t, repoRoot, scenarioPath) + + gotJSON, err := json.MarshalIndent(normalized, "", " ") + require.NoError(t, err) + gotJSON = append(gotJSON, '\n') + + expectedPath := filepath.Join(expectedRoot, scenario+".json") + if *updateScanScenarios { + require.NoError(t, os.MkdirAll(expectedRoot, 0o755)) + require.NoError(t, os.WriteFile(expectedPath, gotJSON, 0o644)) + } + + expectedJSON, err := os.ReadFile(expectedPath) + require.NoErrorf(t, err, "missing golden file for %s; run go test ./test -run TestScanScenarios -update", scenario) + require.JSONEq(t, string(expectedJSON), string(gotJSON)) + }) + } +} + +func runScenarioScan(t *testing.T, repoRoot, scenarioPath string) normalizedSARIF { + t.Helper() + + params, ctx := scan.GetDefaultParameters(context.Background(), repoRoot) + require.NotNil(t, params) + + params.Path = []string{scenarioPath} + params.OutputPath = t.TempDir() + params.OutputName = "scan-scenarios.sarif" + params.RepoPath = repoRoot + params.Platform = []string{"Kubernetes"} + params.FlagEvaluator = featureflags.NewLocalEvaluatorWithOverrides(map[string]bool{ + featureflags.IacEnableKustomizeResolver: true, + }) + params.SCIInfo = model.SCIInfo{ + DiffAware: model.DiffAware{Enabled: false}, + RepositoryCommitInfo: model.RepositoryCommitInfo{ + RepositoryUrl: "https://example.invalid/datadog-iac-scanner", + CommitSHA: "scan-scenarios-test", + Branch: "scan-scenarios", + }, + } + + _, err := console.ExecuteScan(ctx, params) + require.NoError(t, err) + + reportPath := filepath.Join(params.OutputPath, params.OutputName) + data, err := os.ReadFile(reportPath) + require.NoError(t, err) + + return normalizeSARIF(t, data) +} + +func normalizeSARIF(t *testing.T, data []byte) normalizedSARIF { + t.Helper() + + var doc sarifDocument + require.NoError(t, json.Unmarshal(data, &doc)) + require.Len(t, doc.Runs, 1) + + run := doc.Runs[0] + out := normalizedSARIF{ + Rules: make([]normalizedRule, 0, len(run.Tool.Driver.Rules)), + Results: make([]normalizedResult, 0, len(run.Results)), + } + + for _, rule := range run.Tool.Driver.Rules { + out.Rules = append(out.Rules, normalizedRule{ + ID: rule.ID, + Name: rule.Name, + Level: rule.DefaultConfiguration.Level, + HelpURI: rule.HelpURI, + Tags: sortedStringProperty(rule.Properties, "tags"), + }) + } + + for _, result := range run.Results { + nr := normalizedResult{ + RuleID: result.RuleID, + Level: result.Level, + Message: result.Message.Text, + Tags: sortedStringProperty(result.Properties, "tags"), + } + if len(result.Locations) > 0 { + loc := result.Locations[0].PhysicalLocation + nr.ArtifactURI = filepath.ToSlash(loc.ArtifactLocation.ArtifactURI) + nr.StartLine = loc.Region.StartLine + nr.EndLine = loc.Region.EndLine + nr.StartColumn = loc.Region.StartColumn + nr.EndColumn = loc.Region.EndColumn + } + out.Results = append(out.Results, nr) + } + + sort.Slice(out.Rules, func(i, j int) bool { + if out.Rules[i].ID != out.Rules[j].ID { + return out.Rules[i].ID < out.Rules[j].ID + } + return out.Rules[i].Name < out.Rules[j].Name + }) + sort.Slice(out.Results, func(i, j int) bool { + a, b := out.Results[i], out.Results[j] + switch { + case a.RuleID != b.RuleID: + return a.RuleID < b.RuleID + case a.ArtifactURI != b.ArtifactURI: + return a.ArtifactURI < b.ArtifactURI + case a.StartLine != b.StartLine: + return a.StartLine < b.StartLine + case a.StartColumn != b.StartColumn: + return a.StartColumn < b.StartColumn + default: + return a.Message < b.Message + } + }) + + return out +} + +func sortedStringProperty(properties map[string]interface{}, key string) []string { + if len(properties) == 0 { + return nil + } + raw, ok := properties[key] + if !ok { + return nil + } + + items, ok := raw.([]interface{}) + if !ok { + return nil + } + + out := make([]string, 0, len(items)) + for _, item := range items { + s, ok := item.(string) + if !ok { + continue + } + out = append(out, s) + } + sort.Strings(out) + if len(out) == 0 { + return nil + } + return out +}