diff --git a/hooks/declarative-subsequent-scans/hook.test.js b/hooks/declarative-subsequent-scans/hook.test.js index 6f8a5e9e37..17ce2e249d 100644 --- a/hooks/declarative-subsequent-scans/hook.test.js +++ b/hooks/declarative-subsequent-scans/hook.test.js @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 +const { getSubsequentScanDefinition } = require("./scan-helpers"); const { getCascadingScans } = require("./hook"); let parentScan = undefined; @@ -87,6 +88,8 @@ test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () = "--regular", "foobar.com:443", ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, "scanType": "sslyze", }, ] @@ -148,6 +151,8 @@ test("Should not try to do magic to the scan name if its something random", () = "--regular", "10.42.42.42:443", ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, "scanType": "sslyze", }, ] @@ -213,6 +218,8 @@ test("Should not crash when the annotations are not set", () => { "--regular", "foobar.com:443", ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, "scanType": "sslyze", }, ] @@ -267,6 +274,8 @@ test("Should copy ENV fields from cascadingRule to created scan", () => { "--regular", "foobar.com:443", ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, "scanType": "sslyze", }, ] @@ -331,8 +340,451 @@ test("Should allow wildcards in cascadingRules", () => { "--regular", "foobar.com:8443", ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, "scanType": "sslyze", }, ] `); }); + +test("should not copy labels if inheritLabels is set to false", () => { + parentScan.metadata.labels = { + organization: "OWASP", + location: "barcelona", + vlan: "lan" + }; + parentScan.spec.cascades.inheritLabels = false; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => + cascadingScanDefinition.metadata.labels[label] === value + )).toBe(false) + } +}); + +test("should copy labels if inheritLabels is not set", () => { + parentScan.metadata.labels = { + organization: "OWASP", + location: "barcelona", + vlan: "lan" + }; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => + cascadingScanDefinition.metadata.labels[label] === value + )).toBe(true) + } +}); + +test("should copy labels if inheritLabels is set to true", () => { + parentScan.metadata.labels = { + organization: "OWASP", + location: "barcelona", + vlan: "lan" + }; + + parentScan.spec.cascades.inheritLabels = true; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => + cascadingScanDefinition.metadata.labels[label] === value + )).toBe(true) + } +}); + +test("should not copy annotations if inheritAnnotations is set to false", () => { + parentScan.metadata.annotations = { + "defectdojo.securecodebox.io/product-name": "barcelona-network-sca", + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan" + }; + parentScan.spec.cascades.inheritAnnotations = false; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => + cascadingScanDefinition.metadata.annotations[label] === value + )).toBe(false) + } +}); + +test("should copy annotations if inheritAnnotations is not set", () => { + parentScan.metadata.annotations = { + "defectdojo.securecodebox.io/product-name": "barcelona-network-sca", + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan" + }; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => + cascadingScanDefinition.metadata.annotations[label] === value + )).toBe(true) + } +}); + +test("should copy annotations if inheritAnnotations is set to true", () => { + parentScan.metadata.annotations = { + "defectdojo.securecodebox.io/product-name": "barcelona-network-sca", + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan" + }; + parentScan.spec.cascades.inheritAnnotations = true; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => + cascadingScanDefinition.metadata.annotations[label] === value + )).toBe(true) + } +}); + +test("should copy scanLabels from CascadingRule to cascading scan", () => { + sslyzeCascadingRules[0].spec.scanLabels = { + k_one: "v_one", + k_two: "v_two" + } + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } = cascadedScans[0] + + expect(cascadedScans).toMatchInlineSnapshot(` + Array [ + Object { + "cascades": null, + "env": undefined, + "generatedBy": "tls-scans", + "name": "sslyze-foobar.com-tls-scans", + "parameters": Array [ + "--regular", + "foobar.com:443", + ], + "scanAnnotations": Object {}, + "scanLabels": Object { + "k_one": "v_one", + "k_two": "v_two", + }, + "scanType": "sslyze", + }, + ] + `); + + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(sslyzeCascadingRules[0].spec.scanLabels).every(([label, value]) => + cascadingScanDefinition.metadata.labels[label] === value + )).toBe(true) +}); + +test("should copy scanAnnotations from CascadingRule to cascading scan", () => { + sslyzeCascadingRules[0].spec.scanAnnotations = { + k_one: "v_one", + k_two: "v_two" + } + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } = cascadedScans[0] + + expect(cascadedScans).toMatchInlineSnapshot(` + Array [ + Object { + "cascades": null, + "env": undefined, + "generatedBy": "tls-scans", + "name": "sslyze-foobar.com-tls-scans", + "parameters": Array [ + "--regular", + "foobar.com:443", + ], + "scanAnnotations": Object { + "k_one": "v_one", + "k_two": "v_two", + }, + "scanLabels": Object {}, + "scanType": "sslyze", + }, + ] + `); + + const cascadingScanDefinition = getSubsequentScanDefinition({ + name, + parentScan: parentScan, + generatedBy, + scanType, + parameters, + env, + scanLabels, + scanAnnotations + }); + + expect(Object.entries(sslyzeCascadingRules[0].spec.scanAnnotations).every(([label, value]) => + cascadingScanDefinition.metadata.annotations[label] === value + )).toBe(true) +}); + +test("should properly parse template values in scanLabels and scanAnnotations", () => { + sslyzeCascadingRules[0].spec.scanAnnotations = { + k_one: "{{metadata.name}}", + k_two: "{{metadata.unknown_property}}", + k_three: "{{$.hostOrIP}}" + } + + sslyzeCascadingRules[0].spec.scanLabels = { + k_one: "{{metadata.name}}", + k_two: "{{metadata.unknown_property}}", + k_three: "{{$.hostOrIP}}" + } + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https" + } + } + ]; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules + ); + + const { scanLabels, scanAnnotations } = cascadedScans[0] + + // No snapshots as scanLabels/scanAnnotations can be in any order + const result = { + "k_one": "nmap-foobar.com", + "k_two": "", + "k_three": "foobar.com", + } + + expect(scanLabels).toEqual(result) + + expect(scanAnnotations).toEqual(result) +}) + diff --git a/hooks/declarative-subsequent-scans/hook.ts b/hooks/declarative-subsequent-scans/hook.ts index eb54352411..789dd47899 100644 --- a/hooks/declarative-subsequent-scans/hook.ts +++ b/hooks/declarative-subsequent-scans/hook.ts @@ -2,13 +2,14 @@ // // SPDX-License-Identifier: Apache-2.0 -import { isMatch, isMatchWith, isString } from "lodash"; +import { isMatch, isMatchWith, isString, mapValues } from "lodash"; import { isMatch as wildcardIsMatch } from "matcher"; import * as Mustache from "mustache"; import { startSubsequentSecureCodeBoxScan, getCascadingRulesForScan, + getSubsequentScanDefinition, // types Scan, Finding, @@ -27,15 +28,18 @@ export async function handle({ scan, getFindings }: HandleArgs) { const cascadingScans = getCascadingScans(scan, findings, cascadingRules); - for (const { name, scanType, parameters, generatedBy, env } of cascadingScans) { - await startSubsequentSecureCodeBoxScan({ + for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadingScans) { + const cascadingScanDefinition = getSubsequentScanDefinition({ name, parentScan: scan, generatedBy, scanType, parameters, env, + scanLabels, + scanAnnotations }); + await startSubsequentSecureCodeBoxScan(cascadingScanDefinition); } } @@ -88,27 +92,7 @@ export function getCascadingScans( ); if (matches) { - const { scanType, parameters, env } = cascadingRule.spec.scanSpec; - - const templateArgs = { - ...finding, - // Attribute "$" hold special non finding helper attributes - $: { - hostOrIP: - finding.attributes["hostname"] || finding.attributes["ip_address"] - } - }; - - cascadingScans.push({ - name: generateCascadingScanName(parentScan, cascadingRule), - scanType: Mustache.render(scanType, templateArgs), - parameters: parameters.map(parameter => - Mustache.render(parameter, templateArgs) - ), - cascades: null, - generatedBy: cascadingRule.metadata.name, - env, - }); + cascadingScans.push(getCascadingScan(parentScan, finding, cascadingRule)) } } } @@ -116,6 +100,39 @@ export function getCascadingScans( return cascadingScans; } +function getCascadingScan( + parentScan: Scan, + finding: Finding, + cascadingRule: CascadingRule +) { + const { scanType, parameters, env } = cascadingRule.spec.scanSpec; + + const templateArgs = { + ...finding, + ...parentScan, + // Attribute "$" hold special non finding helper attributes + $: { + hostOrIP: + finding.attributes["hostname"] || finding.attributes["ip_address"] + } + }; + + return { + name: generateCascadingScanName(parentScan, cascadingRule), + scanType: Mustache.render(scanType, templateArgs), + parameters: parameters.map(parameter => + Mustache.render(parameter, templateArgs) + ), + cascades: null, + generatedBy: cascadingRule.metadata.name, + env, + scanLabels: cascadingRule.spec.scanLabels === undefined ? {} : + mapValues(cascadingRule.spec.scanLabels, value => Mustache.render(value, templateArgs)), + scanAnnotations: cascadingRule.spec.scanAnnotations === undefined ? {} : + mapValues(cascadingRule.spec.scanAnnotations, value => Mustache.render(value, templateArgs)), + }; +} + function generateCascadingScanName( parentScan: Scan, cascadingRule: CascadingRule diff --git a/hooks/declarative-subsequent-scans/scan-helpers.ts b/hooks/declarative-subsequent-scans/scan-helpers.ts index 695f101c67..a41cc476e6 100644 --- a/hooks/declarative-subsequent-scans/scan-helpers.ts +++ b/hooks/declarative-subsequent-scans/scan-helpers.ts @@ -34,6 +34,12 @@ export interface CascadingRule { export interface CascadingRuleSpec { matches: Matches; scanSpec: ScanSpec; + scanLabels: { + [key: string]: string; + }; + scanAnnotations: { + [key: string]: string; + }; } export interface Matches { @@ -48,10 +54,15 @@ export interface Scan { export interface ScanSpec { scanType: string; parameters: Array; - cascades: LabelSelector; + cascades: LabelSelector & CascadingInheritance; env?: Array; } +export interface CascadingInheritance { + inheritLabels: boolean, + inheritAnnotations: boolean +} + export interface ExtendedScanSpec extends ScanSpec { // This is the name of the scan. Its not "really" part of the scan spec // But this makes the object smaller @@ -59,16 +70,43 @@ export interface ExtendedScanSpec extends ScanSpec { // Indicates which CascadingRule was used to generate the resulting Scan generatedBy: string; + + // Additional label to be added to the resulting scan + scanLabels: { + [key: string]: string; + }; + + // Additional annotations to be added to the resulting scan + scanAnnotations: { + [key: string]: string; + }; } -export async function startSubsequentSecureCodeBoxScan({ - name, - parentScan, - scanType, - parameters, - generatedBy, - env, -}) { +export function getSubsequentScanDefinition({ + name, + parentScan, + scanType, + parameters, + generatedBy, + env, + scanLabels, + scanAnnotations + }) { + function mergeInherited(parentProps, ruleProps, inherit: boolean = true) { + if (!inherit) { + parentProps = {}; + } + return { + ...parentProps, + ...ruleProps // ruleProps overwrites any duplicate keys from parentProps + } + } + + let annotations = mergeInherited( + parentScan.metadata.annotations, scanAnnotations, parentScan.spec.cascades.inheritAnnotations); + let labels = mergeInherited( + parentScan.metadata.labels, scanLabels, parentScan.spec.cascades.inheritLabels); + let cascadingChain: Array = []; if (parentScan.metadata.annotations && parentScan.metadata.annotations["cascading.securecodebox.io/chain"]) { @@ -77,13 +115,13 @@ export async function startSubsequentSecureCodeBoxScan({ ].split(","); } - const scanDefinition = { + return { apiVersion: "execution.securecodebox.io/v1", kind: "Scan", metadata: { generateName: `${name}-`, labels: { - ...parentScan.metadata.labels + ...labels }, annotations: { "securecodebox.io/hook": "declarative-subsequent-scans", @@ -91,7 +129,8 @@ export async function startSubsequentSecureCodeBoxScan({ "cascading.securecodebox.io/chain": [ ...cascadingChain, generatedBy - ].join(",") + ].join(","), + ...annotations, }, ownerReferences: [ { @@ -111,8 +150,10 @@ export async function startSubsequentSecureCodeBoxScan({ env, } }; +} - console.log(`Starting Scan ${name}`); +export async function startSubsequentSecureCodeBoxScan(scan: Scan) { + console.log(`Starting Scan ${scan.metadata.name}`); try { // Submitting the Scan to the kubernetes api @@ -121,11 +162,11 @@ export async function startSubsequentSecureCodeBoxScan({ "v1", namespace, "scans", - scanDefinition, + scan, "false" ); } catch (error) { - console.error(`Failed to start Scan ${name}`); + console.error(`Failed to start Scan ${scan.metadata.name}`); console.error(error); } } diff --git a/hooks/persistence-defectdojo/src/main/java/io/securecodebox/models/V1ScanSpecCascades.java b/hooks/persistence-defectdojo/src/main/java/io/securecodebox/models/V1ScanSpecCascades.java index d0e39a1d67..d93d6798b5 100644 --- a/hooks/persistence-defectdojo/src/main/java/io/securecodebox/models/V1ScanSpecCascades.java +++ b/hooks/persistence-defectdojo/src/main/java/io/securecodebox/models/V1ScanSpecCascades.java @@ -39,6 +39,14 @@ @ApiModel(description = "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.") @javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2021-03-26T19:41:39.505Z[Etc/UTC]") public class V1ScanSpecCascades { + public static final String SERIALIZED_NAME_INHERIT_LABELS = "inheritLabels"; + @SerializedName(SERIALIZED_NAME_INHERIT_LABELS) + private boolean inheritLabels = true; + + public static final String SERIALIZED_NAME_INHERIT_ANNOTATIONS = "inheritAnnotations"; + @SerializedName(SERIALIZED_NAME_INHERIT_ANNOTATIONS) + private boolean inheritAnnotations = true; + public static final String SERIALIZED_NAME_MATCH_EXPRESSIONS = "matchExpressions"; @SerializedName(SERIALIZED_NAME_MATCH_EXPRESSIONS) private List matchExpressions = null; @@ -47,6 +55,21 @@ public class V1ScanSpecCascades { @SerializedName(SERIALIZED_NAME_MATCH_LABELS) private Map matchLabels = null; + public boolean inheritsLabels() { + return inheritsAnnotations(); + } + + public void setInheritLabels(boolean inheritLabels) { + this.inheritLabels = inheritLabels; + } + + public boolean inheritsAnnotations() { + return inheritAnnotations; + } + + public void setInheritAnnotations(boolean inheritAnnotations) { + this.inheritAnnotations = inheritAnnotations; + } public V1ScanSpecCascades matchExpressions(List matchExpressions) { @@ -120,7 +143,9 @@ public boolean equals(Object o) { } V1ScanSpecCascades v1ScanSpecCascades = (V1ScanSpecCascades) o; return Objects.equals(this.matchExpressions, v1ScanSpecCascades.matchExpressions) && - Objects.equals(this.matchLabels, v1ScanSpecCascades.matchLabels); + Objects.equals(this.matchLabels, v1ScanSpecCascades.matchLabels) && + this.inheritLabels == v1ScanSpecCascades.inheritLabels && + this.inheritAnnotations == v1ScanSpecCascades.inheritAnnotations; } @Override @@ -133,6 +158,8 @@ public int hashCode() { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class V1ScanSpecCascades {\n"); + sb.append(" inheritLabels: ").append(String.valueOf(inheritLabels)).append("\n"); + sb.append(" inheritAnnotations: ").append(String.valueOf(inheritAnnotations)).append("\n"); sb.append(" matchExpressions: ").append(toIndentedString(matchExpressions)).append("\n"); sb.append(" matchLabels: ").append(toIndentedString(matchLabels)).append("\n"); sb.append("}"); diff --git a/operator/apis/cascading/v1/cascadingrule_types.go b/operator/apis/cascading/v1/cascadingrule_types.go index 1e79eea391..d0903cefde 100644 --- a/operator/apis/cascading/v1/cascadingrule_types.go +++ b/operator/apis/cascading/v1/cascadingrule_types.go @@ -20,6 +20,15 @@ type CascadingRuleSpec struct { // Matches defines to which findings the CascadingRule should apply Matches Matches `json:"matches"` + + // ScanLabels define additional labels for cascading scans + // +optional + ScanLabels map[string]string `json:"scanLabels"` + + // ScanAnnotations define additional annotations for cascading scans + // +optional + ScanAnnotations map[string]string `json:"scanAnnotations"` + // ScanSpec defines how the cascaded scan should look like ScanSpec executionv1.ScanSpec `json:"scanSpec"` } diff --git a/operator/apis/cascading/v1/zz_generated.deepcopy.go b/operator/apis/cascading/v1/zz_generated.deepcopy.go index eee942ab3c..828fe5cf73 100644 --- a/operator/apis/cascading/v1/zz_generated.deepcopy.go +++ b/operator/apis/cascading/v1/zz_generated.deepcopy.go @@ -76,6 +76,20 @@ func (in *CascadingRuleList) DeepCopyObject() runtime.Object { func (in *CascadingRuleSpec) DeepCopyInto(out *CascadingRuleSpec) { *out = *in in.Matches.DeepCopyInto(&out.Matches) + if in.ScanLabels != nil { + in, out := &in.ScanLabels, &out.ScanLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ScanAnnotations != nil { + in, out := &in.ScanAnnotations, &out.ScanAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } in.ScanSpec.DeepCopyInto(&out.ScanSpec) } diff --git a/operator/apis/execution/v1/scan_types.go b/operator/apis/execution/v1/scan_types.go index 7f8c4e1015..4574ea8daa 100644 --- a/operator/apis/execution/v1/scan_types.go +++ b/operator/apis/execution/v1/scan_types.go @@ -12,6 +12,26 @@ import ( // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. +// CascadeSpec describes how and when cascading scans should be generated. +type CascadeSpec struct { + // InheritLabels defines whether cascading scans should inherit labels from the parent scan + // +optional + InheritLabels bool `json:"inheritLabels,omitempty"` + + // InheritAnnotations defines whether cascading scans should inherit annotations from the parent scan + // +optional + InheritAnnotations bool `json:"inheritAnnotations,omitempty"` + + // matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + // map is equivalent to an element of matchExpressions, whose key field is "key", the + // operator is "In", and the values array contains only "value". The requirements are ANDed. + // +optional + MatchLabels map[string]string `json:"matchLabels,omitempty" protobuf:"bytes,1,rep,name=matchLabels"` + // matchExpressions is a list of label selector requirements. The requirements are ANDed. + // +optional + MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty" protobuf:"bytes,2,rep,name=matchExpressions"` +} + // ScanSpec defines the desired state of Scan type ScanSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster @@ -30,7 +50,7 @@ type ScanSpec struct { // VolumeMounts allows to specify volume mounts for the scan container. VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` - Cascades *metav1.LabelSelector `json:"cascades,omitempty"` + Cascades *CascadeSpec `json:"cascades,omitempty"` } // ScanStatus defines the observed state of Scan diff --git a/operator/apis/execution/v1/zz_generated.deepcopy.go b/operator/apis/execution/v1/zz_generated.deepcopy.go index c9adf576f5..4c9447e9a8 100644 --- a/operator/apis/execution/v1/zz_generated.deepcopy.go +++ b/operator/apis/execution/v1/zz_generated.deepcopy.go @@ -14,6 +14,35 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CascadeSpec) DeepCopyInto(out *CascadeSpec) { + *out = *in + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.MatchExpressions != nil { + in, out := &in.MatchExpressions, &out.MatchExpressions + *out = make([]metav1.LabelSelectorRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CascadeSpec. +func (in *CascadeSpec) DeepCopy() *CascadeSpec { + if in == nil { + return nil + } + out := new(CascadeSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtractResults) DeepCopyInto(out *ExtractResults) { *out = *in @@ -415,9 +444,9 @@ func (in *ScanSpec) DeepCopyInto(out *ScanSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Cascades != nil { - in, out := &in.Cascades, &out.Cascades - *out = new(metav1.LabelSelector) + if in.CascadeSpec != nil { + in, out := &in.CascadeSpec, &out.CascadeSpec + *out = new(CascadeSpec) (*in).DeepCopyInto(*out) } } diff --git a/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml b/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml index 1156bbc6c7..def8987c0f 100644 --- a/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml +++ b/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml @@ -85,15 +85,32 @@ spec: type: object type: array type: object + scanAnnotations: + additionalProperties: + type: string + description: ScanAnnotations define additional annotations for cascading + scans + type: object + scanLabels: + additionalProperties: + type: string + description: ScanLabels define additional labels for cascading scans + type: object scanSpec: description: ScanSpec defines how the cascaded scan should look like properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: CascadeSpec describes how and when cascading scans + should be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/operator/config/crd/bases/execution.securecodebox.io_scans.yaml b/operator/config/crd/bases/execution.securecodebox.io_scans.yaml index 08693e8e5f..1ffdcd8a9d 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scans.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scans.yaml @@ -63,11 +63,17 @@ spec: description: ScanSpec defines the desired state of Scan properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: CascadeSpec describes how and when cascading scans should + be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml index b33e705dc5..3addb8ba4d 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml @@ -77,11 +77,17 @@ spec: description: ScanSpec describes the scan which should be started regularly properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: CascadeSpec describes how and when cascading scans + should be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/operator/crds/cascading.securecodebox.io_cascadingrules.yaml b/operator/crds/cascading.securecodebox.io_cascadingrules.yaml index 1156bbc6c7..d769ab17b7 100644 --- a/operator/crds/cascading.securecodebox.io_cascadingrules.yaml +++ b/operator/crds/cascading.securecodebox.io_cascadingrules.yaml @@ -85,15 +85,32 @@ spec: type: object type: array type: object + scanAnnotations: + additionalProperties: + type: string + description: ScanAnnotations define additional annotations for cascading + scans + type: object + scanLabels: + additionalProperties: + type: string + description: ScanLabels define additional labels for cascading scans + type: object scanSpec: description: ScanSpec defines how the cascaded scan should look like properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: Cascades describes how and when cascading scans should + be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/operator/crds/execution.securecodebox.io_scans.yaml b/operator/crds/execution.securecodebox.io_scans.yaml index 08693e8e5f..1123d570b4 100644 --- a/operator/crds/execution.securecodebox.io_scans.yaml +++ b/operator/crds/execution.securecodebox.io_scans.yaml @@ -63,11 +63,17 @@ spec: description: ScanSpec defines the desired state of Scan properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: Cascades describes how and when cascading scans should + be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. diff --git a/operator/crds/execution.securecodebox.io_scheduledscans.yaml b/operator/crds/execution.securecodebox.io_scheduledscans.yaml index b33e705dc5..824bc2da39 100644 --- a/operator/crds/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/crds/execution.securecodebox.io_scheduledscans.yaml @@ -77,11 +77,17 @@ spec: description: ScanSpec describes the scan which should be started regularly properties: cascades: - description: A label selector is a label query over a set of resources. - The result of matchLabels and matchExpressions are ANDed. An empty - label selector matches all objects. A null label selector matches - no objects. + description: Cascades describes how and when cascading scans should + be generated. properties: + inheritAnnotations: + description: InheritAnnotations defines whether cascading scans + should inherit annotations from the parent scan + type: boolean + inheritLabels: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + type: boolean matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed.