diff --git a/.templates/new-scanner/templates/new-scanner-parse-definition.yaml b/.templates/new-scanner/templates/new-scanner-parse-definition.yaml index b2e8d0a628..2e9a3dc630 100644 --- a/.templates/new-scanner/templates/new-scanner-parse-definition.yaml +++ b/.templates/new-scanner/templates/new-scanner-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/.templates/new-scanner/values.yaml b/.templates/new-scanner/values.yaml index be49b8636f..ef7e16fd64 100644 --- a/.templates/new-scanner/values.yaml +++ b/.templates/new-scanner/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/hooks/cascading-scans/hook/Dockerfile b/hooks/cascading-scans/hook/Dockerfile index 0fb147e958..1552e30e31 100644 --- a/hooks/cascading-scans/hook/Dockerfile +++ b/hooks/cascading-scans/hook/Dockerfile @@ -15,10 +15,10 @@ RUN mkdir -p /home/app WORKDIR /home/app COPY package.json package-lock.json ./ RUN npm ci -COPY hook.ts scan-helpers.ts kubernetes-label-selector.ts ./ +COPY hook.ts scan-helpers.ts scope-limiter.ts kubernetes-label-selector.ts ./ RUN npm run build FROM ${namespace:-securecodebox}/hook-sdk-nodejs:${baseImageTag:-latest} WORKDIR /home/app/hook-wrapper/hook/ COPY --from=install --chown=app:app /home/app/node_modules/ ./node_modules/ -COPY --from=build --chown=app:app /home/app/hook.js /home/app/hook.js.map /home/app/scan-helpers.js /home/app/scan-helpers.js.map /home/app/kubernetes-label-selector.js /home/app/kubernetes-label-selector.js.map ./ +COPY --from=build --chown=app:app /home/app/hook.js /home/app/hook.js.map /home/app/scan-helpers.js /home/app/scan-helpers.js.map /home/app/scope-limiter.js /home/app/scope-limiter.js.map /home/app/kubernetes-label-selector.js /home/app/kubernetes-label-selector.js.map ./ diff --git a/hooks/cascading-scans/hook/hook.test.js b/hooks/cascading-scans/hook/hook.test.js index c42eeba05c..719a814cd4 100644 --- a/hooks/cascading-scans/hook/hook.test.js +++ b/hooks/cascading-scans/hook/hook.test.js @@ -3,10 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 const { getCascadingScans } = require("./hook"); -const {LabelSelectorRequirementOperator} = require("./kubernetes-label-selector"); +const { + LabelSelectorRequirementOperator, +} = require("./kubernetes-label-selector"); +const { ScopeLimiterRequirementOperator } = require("./scope-limiter"); let parentScan = undefined; let sslyzeCascadingRules = undefined; +let parseDefinition = undefined; beforeEach(() => { parentScan = { @@ -14,13 +18,19 @@ beforeEach(() => { kind: "Scan", metadata: { name: "nmap-foobar.com", - annotations: {} + annotations: {}, }, spec: { scanType: "nmap", parameters: "foobar.com", - cascades: {} - } + cascades: {}, + }, + }; + parseDefinition = { + meta: {}, + spec: { + scopeLimiterAliases: {}, + }, }; sslyzeCascadingRules = [ @@ -28,7 +38,7 @@ beforeEach(() => { apiVersion: "cascading.securecodebox.io/v1", kind: "CascadingRule", metadata: { - name: "tls-scans" + name: "tls-scans", }, spec: { matches: { @@ -37,23 +47,23 @@ beforeEach(() => { category: "Open Port", attributes: { port: 443, - service: "https" - } + service: "https", + }, }, { category: "Open Port", attributes: { - service: "https" - } - } - ] + service: "https", + }, + }, + ], }, scanSpec: { scanType: "sslyze", - parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"] - } - } - } + parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], + }, + }, + }, ]; }); @@ -66,15 +76,17 @@ test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () = state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -131,14 +143,20 @@ test("Should create no subsequent scans if there are no rules", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadingRules = []; - const cascadedScans = getCascadingScans(parentScan, findings, cascadingRules); + const cascadedScans = getCascadingScans( + parentScan, + findings, + cascadingRules, + undefined, + parseDefinition + ); expect(cascadedScans).toMatchInlineSnapshot(`Array []`); }); @@ -155,18 +173,22 @@ test("Should not try to do magic to the scan name if its something random", () = hostname: undefined, ip_address: "10.42.42.42", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - expect(cascadedScans[0].metadata.generateName).toEqual("foobar.com-tls-scans-"); + expect(cascadedScans[0].metadata.generateName).toEqual( + "foobar.com-tls-scans-" + ); }); test("Should not start a new scan when the corresponding cascadingRule is already in the chain", () => { @@ -181,15 +203,17 @@ test("Should not start a new scan when the corresponding cascadingRule is alread state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(`Array []`); @@ -206,15 +230,17 @@ test("Should not crash when the annotations are not set", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -266,8 +292,8 @@ test("Should copy ENV fields from cascadingRule to created scan", () => { sslyzeCascadingRules[0].spec.scanSpec.env = [ { name: "FOOBAR", - valueFrom: { secretKeyRef: { name: "foobar-token", key: "token" } } - } + valueFrom: { secretKeyRef: { name: "foobar-token", key: "token" } }, + }, ]; const findings = [ @@ -278,15 +304,17 @@ test("Should copy ENV fields from cascadingRule to created scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans[0].spec.env).toMatchInlineSnapshot(` @@ -310,7 +338,7 @@ test("Should allow wildcards in cascadingRules", () => { apiVersion: "cascading.securecodebox.io/v1", kind: "CascadingRule", metadata: { - name: "tls-scans" + name: "tls-scans", }, spec: { matches: { @@ -319,17 +347,17 @@ test("Should allow wildcards in cascadingRules", () => { category: "Open Port", attributes: { port: 8443, - service: "https*" - } - } - ] + service: "https*", + }, + }, + ], }, scanSpec: { scanType: "sslyze", - parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"] - } - } - } + parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], + }, + }, + }, ]; const findings = [ @@ -340,15 +368,17 @@ test("Should allow wildcards in cascadingRules", () => { state: "open", hostname: "foobar.com", port: 8443, - service: "https-alt" - } - } + service: "https-alt", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -400,7 +430,7 @@ test("should not copy labels if inheritLabels is set to false", () => { parentScan.metadata.labels = { organization: "OWASP", location: "barcelona", - vlan: "lan" + vlan: "lan", }; parentScan.spec.cascades.inheritLabels = false; @@ -412,21 +442,25 @@ test("should not copy labels if inheritLabels is set to false", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => - cascadedScan.metadata.labels[label] === value - )).toBe(false) + expect( + Object.entries(parentScan.metadata.labels).every( + ([label, value]) => cascadedScan.metadata.labels[label] === value + ) + ).toBe(false); } }); @@ -434,7 +468,7 @@ test("should copy labels if inheritLabels is not set", () => { parentScan.metadata.labels = { organization: "OWASP", location: "barcelona", - vlan: "lan" + vlan: "lan", }; const findings = [ @@ -445,21 +479,25 @@ test("should copy labels if inheritLabels is not set", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => - cascadedScan.metadata.labels[label] === value - )).toBe(true) + expect( + Object.entries(parentScan.metadata.labels).every( + ([label, value]) => cascadedScan.metadata.labels[label] === value + ) + ).toBe(true); } }); @@ -467,7 +505,7 @@ test("should copy labels if inheritLabels is set to true", () => { parentScan.metadata.labels = { organization: "OWASP", location: "barcelona", - vlan: "lan" + vlan: "lan", }; parentScan.spec.cascades.inheritLabels = true; @@ -480,28 +518,32 @@ test("should copy labels if inheritLabels is set to true", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => - cascadedScan.metadata.labels[label] === value - )).toBe(true) + expect( + Object.entries(parentScan.metadata.labels).every( + ([label, value]) => cascadedScan.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" + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan", }; parentScan.spec.cascades.inheritAnnotations = false; @@ -513,28 +555,32 @@ test("should not copy annotations if inheritAnnotations is set to false", () => state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => - cascadedScan.metadata.annotations[label] === value - )).toBe(false) + expect( + Object.entries(parentScan.metadata.annotations).every( + ([label, value]) => cascadedScan.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" + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan", }; const findings = [ @@ -545,28 +591,32 @@ test("should copy annotations if inheritAnnotations is not set", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => - cascadedScan.metadata.annotations[label] === value - )).toBe(true) + expect( + Object.entries(parentScan.metadata.annotations).every( + ([label, value]) => cascadedScan.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" + "defectdojo.securecodebox.io/engagement-name": "scb-automated-scan", }; parentScan.spec.cascades.inheritAnnotations = true; @@ -578,29 +628,33 @@ test("should copy annotations if inheritAnnotations is set to true", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); for (const cascadedScan of cascadedScans) { - expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => - cascadedScan.metadata.annotations[label] === value - )).toBe(true) + expect( + Object.entries(parentScan.metadata.annotations).every( + ([label, value]) => cascadedScan.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" - } + k_two: "v_two", + }; const findings = [ { @@ -610,28 +664,32 @@ test("should copy scanLabels from CascadingRule to cascading scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] - expect(Object.entries(sslyzeCascadingRules[0].spec.scanLabels).every(([label, value]) => - cascadedScan.metadata.labels[label] === value - )).toBe(true) + const cascadedScan = cascadedScans[0]; + expect( + Object.entries(sslyzeCascadingRules[0].spec.scanLabels).every( + ([label, value]) => cascadedScan.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" - } + k_two: "v_two", + }; const findings = [ { @@ -641,35 +699,39 @@ test("should copy scanAnnotations from CascadingRule to cascading scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] - expect(Object.entries(sslyzeCascadingRules[0].spec.scanAnnotations).every(([label, value]) => - cascadedScan.metadata.annotations[label] === value - )).toBe(true) + const cascadedScan = cascadedScans[0]; + expect( + Object.entries(sslyzeCascadingRules[0].spec.scanAnnotations).every( + ([label, value]) => cascadedScan.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}}" - } + k_three: "{{$.hostOrIP}}", + }; sslyzeCascadingRules[0].spec.scanLabels = { k_one: "{{metadata.name}}", k_two: "{{metadata.unknown_property}}", - k_three: "{{$.hostOrIP}}" - } + k_three: "{{$.hostOrIP}}", + }; const findings = [ { @@ -679,8 +741,8 @@ test("should properly parse template values in scanLabels and scanAnnotations", state: "open", hostname: "foobar.com", port: 8443, - service: "https" - } + service: "https", + }, }, { name: "Port 443 is open", @@ -689,43 +751,47 @@ test("should properly parse template values in scanLabels and scanAnnotations", state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, sslyzeCascadingRules, - sslyzeCascadingRules[0] + sslyzeCascadingRules[0], + parseDefinition ); - expect(sslyzeCascadingRules[0].spec.scanSpec.parameters).toEqual(["--regular", "{{$.hostOrIP}}:{{attributes.port}}"]) + expect(sslyzeCascadingRules[0].spec.scanSpec.parameters).toEqual([ + "--regular", + "{{$.hostOrIP}}:{{attributes.port}}", + ]); const { labels, annotations } = cascadedScans[0].metadata; // No snapshots as scanLabels/scanAnnotations can be in any order const labelResults = { - "k_one": "nmap-foobar.com", - "k_two": "", - "k_three": "foobar.com", - } + k_one: "nmap-foobar.com", + k_two: "", + k_three: "foobar.com", + }; - expect(labels).toEqual(labelResults) + expect(labels).toEqual(labelResults); const annotationsResults = { "cascading.securecodebox.io/chain": "tls-scans", "cascading.securecodebox.io/matched-finding": undefined, "cascading.securecodebox.io/parent-scan": "nmap-foobar.com", "securecodebox.io/hook": "cascading-scans", - "k_one": "nmap-foobar.com", - "k_two": "", - "k_three": "foobar.com", + k_one: "nmap-foobar.com", + k_two: "", + k_three: "foobar.com", }; - expect(annotations).toEqual(annotationsResults) -}) + expect(annotations).toEqual(annotationsResults); +}); test("should copy proper finding ID into annotations", () => { const findings = [ @@ -736,9 +802,9 @@ test("should copy proper finding ID into annotations", () => { state: "open", hostname: "foobar.com", port: 12345, - service: "unknown" + service: "unknown", }, - id: "random-id" + id: "random-id", }, { name: "Port 443 is open", @@ -747,29 +813,34 @@ test("should copy proper finding ID into annotations", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" + service: "https", }, - id: "f0c718bd-9987-42c8-2259-73794e61dd5a" - } + id: "f0c718bd-9987-42c8-2259-73794e61dd5a", + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] - expect(Object.entries(cascadedScan.metadata.annotations).every(([label, value]) => { - if (label === "cascading.securecodebox.io/matched-finding") { - return value === "f0c718bd-9987-42c8-2259-73794e61dd5a"; - } else return true; - } - )).toBe(true) + const cascadedScan = cascadedScans[0]; + expect( + Object.entries(cascadedScan.metadata.annotations).every( + ([label, value]) => { + if (label === "cascading.securecodebox.io/matched-finding") { + return value === "f0c718bd-9987-42c8-2259-73794e61dd5a"; + } else return true; + } + ) + ).toBe(true); }); test("should merge environment variables into cascaded scan", () => { - parentScan.spec.cascades.inheritEnv = true + parentScan.spec.cascades.inheritEnv = true; const findings = [ { name: "Port 443 is open", @@ -778,32 +849,34 @@ test("should merge environment variables into cascaded scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.env = [ { - "name": "parent_environment_variable_name", - "value": "parent_environment_variable_value" - } - ] + name: "parent_environment_variable_name", + value: "parent_environment_variable_value", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.env = [ { - "name": "rule_environment_variable_name", - "value": "rule_environment_variable_value" - } - ] + name: "rule_environment_variable_name", + value: "rule_environment_variable_value", + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] + const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.env).toMatchInlineSnapshot(` Array [ Object { @@ -819,7 +892,7 @@ test("should merge environment variables into cascaded scan", () => { }); test("should merge volumeMounts into cascaded scan", () => { - parentScan.spec.cascades.inheritVolumes = true + parentScan.spec.cascades.inheritVolumes = true; const findings = [ { name: "Port 443 is open", @@ -828,36 +901,38 @@ test("should merge volumeMounts into cascaded scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert.cer", - "name": "ca-certificate", - "readOnly": true, - "subPath": "ca-cert.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert.cer", + name: "ca-certificate", + readOnly: true, + subPath: "ca-cert.cer", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert-sslyze.cer", - "name": "ca-certificate-sslyze", - "readOnly": true, - "subPath": "ca-cert-sslyze.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert-sslyze.cer", + name: "ca-certificate-sslyze", + readOnly: true, + subPath: "ca-cert-sslyze.cer", + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] + const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.volumeMounts).toMatchInlineSnapshot(` Array [ Object { @@ -877,7 +952,7 @@ test("should merge volumeMounts into cascaded scan", () => { }); test("should merge volumes into cascaded scan", () => { - parentScan.spec.cascades.inheritVolumes = true + parentScan.spec.cascades.inheritVolumes = true; const findings = [ { name: "Port 443 is open", @@ -886,33 +961,35 @@ test("should merge volumes into cascaded scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.volumes = [ { - "name": "ca-certificate", - "configMap": { - "name": "ca-certificate" - } - } - ] + name: "ca-certificate", + configMap: { + name: "ca-certificate", + }, + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumes = [ { - "name": "ca-certificate-sslyze", - "configMap": { - "name": "ca-certificate-sslyze" - } - } - ] + name: "ca-certificate-sslyze", + configMap: { + name: "ca-certificate-sslyze", + }, + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; @@ -936,7 +1013,7 @@ test("should merge volumes into cascaded scan", () => { }); test("should merge initContainers into cascaded scan", () => { - parentScan.spec.cascades.inheritInitContainers = true + parentScan.spec.cascades.inheritInitContainers = true; const findings = [ { name: "Port 443 is open", @@ -945,54 +1022,56 @@ test("should merge initContainers into cascaded scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.initContainers = [ { - "name": "test-init", - "image": "bitnami/git", - "command": ["whoami"] - } - ] + name: "test-init", + image: "bitnami/git", + command: ["whoami"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.initContainers = [ { - "name": "test-init-2", - "image": "some/hypothetical", - "command": ["echo", "1"] - } - ] + name: "test-init-2", + image: "some/hypothetical", + command: ["echo", "1"], + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.initContainers).toMatchInlineSnapshot(` - Array [ - Object { - "command": Array [ - "whoami", - ], - "image": "bitnami/git", - "name": "test-init", - }, - Object { - "command": Array [ - "echo", - "1", - ], - "image": "some/hypothetical", - "name": "test-init-2", - }, - ] - `); + Array [ + Object { + "command": Array [ + "whoami", + ], + "image": "bitnami/git", + "name": "test-init", + }, + Object { + "command": Array [ + "echo", + "1", + ], + "image": "some/hypothetical", + "name": "test-init-2", + }, + ] + `); }); test("should not merge initContainers into cascaded scan if not instructed", () => { @@ -1004,47 +1083,49 @@ test("should not merge initContainers into cascaded scan if not instructed", () state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.initContainers = [ { - "name": "test-init", - "image": "bitnami/git", - "command": ["whoami"] - } - ] + name: "test-init", + image: "bitnami/git", + command: ["whoami"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.initContainers = [ { - "name": "test-init-2", - "image": "some/hypothetical", - "command": ["echo", "1"] - } - ] + name: "test-init-2", + image: "some/hypothetical", + command: ["echo", "1"], + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.initContainers).toMatchInlineSnapshot(` - Array [ - Object { - "command": Array [ - "echo", - "1", - ], - "image": "some/hypothetical", - "name": "test-init-2", - }, - ] - `); + Array [ + Object { + "command": Array [ + "echo", + "1", + ], + "image": "some/hypothetical", + "name": "test-init-2", + }, + ] + `); }); test("Templating should apply to environment variables", () => { @@ -1091,7 +1172,7 @@ test("Templating should apply to environment variables", () => { parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], volumes: [{ name: "test-volume", emptyDir: {} }], volumeMounts: [{ name: "test-volume", mountPath: "/test" }], - env: [{"name": "HostOrIp", "value": "{{$.hostOrIP}}"}], + env: [{ name: "HostOrIp", value: "{{$.hostOrIP}}" }], }, }, }, @@ -1100,7 +1181,9 @@ test("Templating should apply to environment variables", () => { const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -1223,7 +1306,9 @@ test("Templating should apply to initContainer commands", () => { const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -1348,7 +1433,7 @@ test("Templating should apply to initContainer environment variables", () => { image: "busybox", command: ["whoami"], volumeMounts: [{ name: "test-volume", mountPath: "/test" }], - env: [{"name": "HostOrIp", "value": "{{$.hostOrIP}}"}], + env: [{ name: "HostOrIp", value: "{{$.hostOrIP}}" }], }, ], }, @@ -1359,7 +1444,9 @@ test("Templating should apply to initContainer environment variables", () => { const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -1497,7 +1584,9 @@ test("Templating should not break special encoding (http://...) when using tripl const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); expect(cascadedScans).toMatchInlineSnapshot(` @@ -1573,7 +1662,7 @@ test("Templating should not break special encoding (http://...) when using tripl }); test("should merge hookSelector into cascaded scan if inheritHookSelector is enabled", () => { - parentScan.spec.cascades.inheritHookSelector = true + parentScan.spec.cascades.inheritHookSelector = true; const findings = [ { name: "Port 443 is open", @@ -1582,72 +1671,73 @@ test("should merge hookSelector into cascaded scan if inheritHookSelector is ena state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; - parentScan.spec.hookSelector = {} + parentScan.spec.hookSelector = {}; parentScan.spec.hookSelector.matchLabels = { "securecodebox.io/internal": "true", - } + }; parentScan.spec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.In, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {}; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.NotIn, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = { "securecodebox.io/internal": "false", - } + }; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.hookSelector).toMatchInlineSnapshot(` - Object { - "matchExpressions": Array [ - Object { - "key": "securecodebox.io/name", - "operator": "In", - "values": Array [ - "cascading-scans", - ], - }, Object { - "key": "securecodebox.io/name", - "operator": "NotIn", - "values": Array [ - "cascading-scans", + "matchExpressions": Array [ + Object { + "key": "securecodebox.io/name", + "operator": "In", + "values": Array [ + "cascading-scans", + ], + }, + Object { + "key": "securecodebox.io/name", + "operator": "NotIn", + "values": Array [ + "cascading-scans", + ], + }, ], - }, - ], - "matchLabels": Object { - "securecodebox.io/internal": "false", - }, - } - `); + "matchLabels": Object { + "securecodebox.io/internal": "false", + }, + } + `); }); - test("should not merge hookSelector into cascaded scan if inheritHookSelector is disabled", () => { - parentScan.spec.cascades.inheritHookSelector = false + parentScan.spec.cascades.inheritHookSelector = false; const findings = [ { name: "Port 443 is open", @@ -1656,60 +1746,62 @@ test("should not merge hookSelector into cascaded scan if inheritHookSelector is state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; - parentScan.spec.hookSelector = {} + parentScan.spec.hookSelector = {}; parentScan.spec.hookSelector.matchLabels = { "securecodebox.io/internal": "true", - } + }; parentScan.spec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.In, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {}; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.NotIn, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = { "securecodebox.io/internal": "false", - } + }; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.hookSelector).toMatchInlineSnapshot(` - Object { - "matchExpressions": Array [ Object { - "key": "securecodebox.io/name", - "operator": "NotIn", - "values": Array [ - "cascading-scans", + "matchExpressions": Array [ + Object { + "key": "securecodebox.io/name", + "operator": "NotIn", + "values": Array [ + "cascading-scans", + ], + }, ], - }, - ], - "matchLabels": Object { - "securecodebox.io/internal": "false", - }, - } - `); + "matchLabels": Object { + "securecodebox.io/internal": "false", + }, + } + `); }); test("should copy tolerations and affinity into cascaded scan if one is set and label is unset", () => { @@ -1721,80 +1813,80 @@ test("should copy tolerations and affinity into cascaded scan if one is set and state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "disktype", operator: "In", - values: [ - "ssd" - ] - } - ] - } - ] - } - } - } - - parentScan.spec.tolerations = [ - { + values: ["ssd"], + }, + ], + }, + ], + }, + }, + }; + + parentScan.spec.tolerations = [ + { key: "key1", operator: "Equal", value: "test", - effect: "NoSchedule" - } - ] + effect: "NoSchedule", + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(` - Object { - "nodeAffinity": Object { - "requiredDuringSchedulingIgnoredDuringExecution": Object { - "nodeSelectorTerms": Array [ - Object { - "matchExpressions": Array [ + Object { + "nodeAffinity": Object { + "requiredDuringSchedulingIgnoredDuringExecution": Object { + "nodeSelectorTerms": Array [ Object { - "key": "disktype", - "operator": "In", - "values": Array [ - "ssd", + "matchExpressions": Array [ + Object { + "key": "disktype", + "operator": "In", + "values": Array [ + "ssd", + ], + }, ], }, ], }, - ], - }, - }, - } - `); + }, + } + `); expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(` - Array [ - Object { - "effect": "NoSchedule", - "key": "key1", - "operator": "Equal", - "value": "test", - }, - ] - `); + Array [ + Object { + "effect": "NoSchedule", + "key": "key1", + "operator": "Equal", + "value": "test", + }, + ] + `); }); test("should not copy tolerations and affinity into cascaded scan if label disables it", () => { @@ -1808,44 +1900,44 @@ test("should not copy tolerations and affinity into cascaded scan if label disab state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "disktype", operator: "In", - values: [ - "ssd" - ] - } - ] - } - ] - } - } - } + values: ["ssd"], + }, + ], + }, + ], + }, + }, + }; parentScan.spec.tolerations = [ { key: "key1", operator: "Equal", value: "test", - effect: "NoSchedule" - } - ] + effect: "NoSchedule", + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; @@ -1864,39 +1956,37 @@ test("should merge tolerations and replace affinity in cascaded scan if cascadin state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "disktype", operator: "In", - values: [ - "ssd" - ] - } - ] - } - ] - } - } - } + values: ["ssd"], + }, + ], + }, + ], + }, + }, + }; parentScan.spec.tolerations = [ { key: "key1", operator: "Equal", value: "test", - effect: "NoSchedule" - } - ] + effect: "NoSchedule", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.tolerations = [ { @@ -1904,76 +1994,76 @@ test("should merge tolerations and replace affinity in cascaded scan if cascadin operator: "Equal", value: "test-2", effect: "NoSchedule", - } + }, ]; sslyzeCascadingRules[0].spec.scanSpec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "network", operator: "In", - values: [ - "10g" - ] - } - ] - } - ] - } - } - } + values: ["10g"], + }, + ], + }, + ], + }, + }, + }; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; // New values will completely replace the old values, not be merged expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(` - Object { - "nodeAffinity": Object { - "requiredDuringSchedulingIgnoredDuringExecution": Object { - "nodeSelectorTerms": Array [ - Object { - "matchExpressions": Array [ + Object { + "nodeAffinity": Object { + "requiredDuringSchedulingIgnoredDuringExecution": Object { + "nodeSelectorTerms": Array [ Object { - "key": "network", - "operator": "In", - "values": Array [ - "10g", + "matchExpressions": Array [ + Object { + "key": "network", + "operator": "In", + "values": Array [ + "10g", + ], + }, ], }, ], }, - ], - }, - }, - } - `); + }, + } + `); expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(` - Array [ - Object { - "effect": "NoSchedule", - "key": "key1", - "operator": "Equal", - "value": "test", - }, - Object { - "effect": "NoSchedule", - "key": "key2", - "operator": "Equal", - "value": "test-2", - }, - ] - `); + Array [ + Object { + "effect": "NoSchedule", + "key": "key1", + "operator": "Equal", + "value": "test", + }, + Object { + "effect": "NoSchedule", + "key": "key2", + "operator": "Equal", + "value": "test-2", + }, + ] + `); }); test("should not set affinity or tolerations to undefined if they are defined to be an empty map / list in upstream scan", () => { @@ -1985,19 +2075,21 @@ test("should not set affinity or tolerations to undefined if they are defined to state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; - parentScan.spec.affinity = {} + parentScan.spec.affinity = {}; - parentScan.spec.tolerations = [] + parentScan.spec.tolerations = []; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; @@ -2017,9 +2109,9 @@ test("Should not set affinity or tolerations to undefined if they are defined to state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; sslyzeCascadingRules[0].spec.scanSpec.tolerations = []; @@ -2029,7 +2121,9 @@ test("Should not set affinity or tolerations to undefined if they are defined to const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; @@ -2051,39 +2145,37 @@ test("should only use tolerations and affinity of cascaded scan if inheritance i state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "disktype", operator: "In", - values: [ - "ssd" - ] - } - ] - } - ] - } - } - } + values: ["ssd"], + }, + ], + }, + ], + }, + }, + }; parentScan.spec.tolerations = [ { key: "key1", operator: "Equal", value: "test", - effect: "NoSchedule" - } - ] + effect: "NoSchedule", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.tolerations = [ { @@ -2091,75 +2183,75 @@ test("should only use tolerations and affinity of cascaded scan if inheritance i operator: "Equal", value: "test-2", effect: "NoSchedule", - } + }, ]; sslyzeCascadingRules[0].spec.scanSpec.affinity = { nodeAffinity: { - requiredDuringSchedulingIgnoredDuringExecution: { + requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ - { + { matchExpressions: [ { key: "network", operator: "In", - values: [ - "10g" - ] - } - ] - } - ] - } - } - } + values: ["10g"], + }, + ], + }, + ], + }, + }, + }; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); const cascadedScan = cascadedScans[0]; expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(` - Object { - "nodeAffinity": Object { - "requiredDuringSchedulingIgnoredDuringExecution": Object { - "nodeSelectorTerms": Array [ - Object { - "matchExpressions": Array [ + Object { + "nodeAffinity": Object { + "requiredDuringSchedulingIgnoredDuringExecution": Object { + "nodeSelectorTerms": Array [ Object { - "key": "network", - "operator": "In", - "values": Array [ - "10g", + "matchExpressions": Array [ + Object { + "key": "network", + "operator": "In", + "values": Array [ + "10g", + ], + }, ], }, ], }, - ], - }, - }, - } - `); + }, + } + `); expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(` - Array [ - Object { - "effect": "NoSchedule", - "key": "key2", - "operator": "Equal", - "value": "test-2", - }, - ] - `); + Array [ + Object { + "effect": "NoSchedule", + "key": "key2", + "operator": "Equal", + "value": "test-2", + }, + ] + `); }); test("should purge cascaded scan spec from parent scan", () => { - parentScan.spec.cascades.inheritEnv = true - parentScan.spec.cascades.inheritVolumes = true - parentScan.spec.cascades.inheritHookSelector = true + parentScan.spec.cascades.inheritEnv = true; + parentScan.spec.cascades.inheritVolumes = true; + parentScan.spec.cascades.inheritHookSelector = true; const findings = [ { name: "Port 443 is open", @@ -2168,100 +2260,102 @@ test("should purge cascaded scan spec from parent scan", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.volumes = [ { - "name": "ca-certificate", - "configMap": { - "name": "ca-certificate" - } - } - ] + name: "ca-certificate", + configMap: { + name: "ca-certificate", + }, + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumes = [ { - "name": "ca-certificate-sslyze", - "configMap": { - "name": "ca-certificate-sslyze" - } - } - ] + name: "ca-certificate-sslyze", + configMap: { + name: "ca-certificate-sslyze", + }, + }, + ]; parentScan.spec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert.cer", - "name": "ca-certificate", - "readOnly": true, - "subPath": "ca-cert.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert.cer", + name: "ca-certificate", + readOnly: true, + subPath: "ca-cert.cer", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert-sslyze.cer", - "name": "ca-certificate-sslyze", - "readOnly": true, - "subPath": "ca-cert-sslyze.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert-sslyze.cer", + name: "ca-certificate-sslyze", + readOnly: true, + subPath: "ca-cert-sslyze.cer", + }, + ]; parentScan.spec.env = [ { - "name": "parent_environment_variable_name", - "value": "parent_environment_variable_value" - } - ] + name: "parent_environment_variable_name", + value: "parent_environment_variable_value", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.env = [ { - "name": "rule_environment_variable_name", - "value": "rule_environment_variable_value" - } - ] + name: "rule_environment_variable_name", + value: "rule_environment_variable_value", + }, + ]; - parentScan.spec.hookSelector = {} + parentScan.spec.hookSelector = {}; parentScan.spec.hookSelector.matchLabels = { "securecodebox.io/internal": "true", - } + }; parentScan.spec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.In, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {}; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [ { key: "securecodebox.io/name", operator: LabelSelectorRequirementOperator.NotIn, - values: ["cascading-scans"] - } - ] + values: ["cascading-scans"], + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = { "securecodebox.io/internal": "false", - } + }; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] + const cascadedScan = cascadedScans[0]; // Create a second cascading rule sslyzeCascadingRules[1] = { apiVersion: "cascading.securecodebox.io/v1", kind: "CascadingRule", metadata: { - name: "tls-scans-second" + name: "tls-scans-second", }, spec: { matches: { @@ -2270,31 +2364,32 @@ test("should purge cascaded scan spec from parent scan", () => { category: "Open Port", attributes: { port: 443, - service: "https" - } + service: "https", + }, }, { category: "Open Port", attributes: { - service: "https" - } - } - ] + service: "https", + }, + }, + ], }, scanSpec: { scanType: "sslyze", - parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"] - } - } - } + parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], + }, + }, + }; - cascadedScan.metadata.name = cascadedScan.metadata.generateName + cascadedScan.metadata.name = cascadedScan.metadata.generateName; const secondCascadedScans = getCascadingScans( cascadedScan, findings, sslyzeCascadingRules, - sslyzeCascadingRules[0] // cascaded rule on parent + sslyzeCascadingRules[0], // cascaded rule on parent + parseDefinition ); const secondCascadedScan = secondCascadedScans[0]; @@ -2306,7 +2401,7 @@ test("should purge cascaded scan spec from parent scan", () => { "value": "parent_environment_variable_value", }, ] - `) + `); expect(secondCascadedScan.spec.volumes).toMatchInlineSnapshot(` Array [ @@ -2317,7 +2412,7 @@ test("should purge cascaded scan spec from parent scan", () => { "name": "ca-certificate", }, ] - `) + `); expect(secondCascadedScan.spec.volumeMounts).toMatchInlineSnapshot(` Array [ @@ -2328,20 +2423,23 @@ test("should purge cascaded scan spec from parent scan", () => { "subPath": "ca-cert.cer", }, ] - `) - - expect(secondCascadedScan.spec.hookSelector.matchExpressions).toMatchInlineSnapshot(` - Array [ - Object { - "key": "securecodebox.io/name", - "operator": "In", - "values": Array [ - "cascading-scans", - ], - }, - ] - `) - expect(secondCascadedScan.spec.hookSelector.matchLabels).toMatchInlineSnapshot(`Object {}`) + `); + + expect(secondCascadedScan.spec.hookSelector.matchExpressions) + .toMatchInlineSnapshot(` + Array [ + Object { + "key": "securecodebox.io/name", + "operator": "In", + "values": Array [ + "cascading-scans", + ], + }, + ] + `); + expect( + secondCascadedScan.spec.hookSelector.matchLabels + ).toMatchInlineSnapshot(`Object {}`); }); test("should not copy cascaded scan spec from parent scan if inheritance is undefined", () => { @@ -2353,74 +2451,76 @@ test("should not copy cascaded scan spec from parent scan if inheritance is unde state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; parentScan.spec.volumes = [ { - "name": "ca-certificate", - "configMap": { - "name": "ca-certificate" - } - } - ] + name: "ca-certificate", + configMap: { + name: "ca-certificate", + }, + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumes = [ { - "name": "ca-certificate-sslyze", - "configMap": { - "name": "ca-certificate-sslyze" - } - } - ] + name: "ca-certificate-sslyze", + configMap: { + name: "ca-certificate-sslyze", + }, + }, + ]; parentScan.spec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert.cer", - "name": "ca-certificate", - "readOnly": true, - "subPath": "ca-cert.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert.cer", + name: "ca-certificate", + readOnly: true, + subPath: "ca-cert.cer", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.volumeMounts = [ { - "mountPath": "/etc/ssl/certs/ca-cert-sslyze.cer", - "name": "ca-certificate-sslyze", - "readOnly": true, - "subPath": "ca-cert-sslyze.cer" - } - ] + mountPath: "/etc/ssl/certs/ca-cert-sslyze.cer", + name: "ca-certificate-sslyze", + readOnly: true, + subPath: "ca-cert-sslyze.cer", + }, + ]; parentScan.spec.env = [ { - "name": "parent_environment_variable_name", - "value": "parent_environment_variable_value" - } - ] + name: "parent_environment_variable_name", + value: "parent_environment_variable_value", + }, + ]; sslyzeCascadingRules[0].spec.scanSpec.env = [ { - "name": "rule_environment_variable_name", - "value": "rule_environment_variable_value" - } - ] + name: "rule_environment_variable_name", + value: "rule_environment_variable_value", + }, + ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] + const cascadedScan = cascadedScans[0]; // Create a second cascading rule sslyzeCascadingRules[1] = { apiVersion: "cascading.securecodebox.io/v1", kind: "CascadingRule", metadata: { - name: "tls-scans-second" + name: "tls-scans-second", }, spec: { matches: { @@ -2429,44 +2529,45 @@ test("should not copy cascaded scan spec from parent scan if inheritance is unde category: "Open Port", attributes: { port: 443, - service: "https" - } + service: "https", + }, }, { category: "Open Port", attributes: { - service: "https" - } - } - ] + service: "https", + }, + }, + ], }, scanSpec: { scanType: "sslyze", - parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"] - } - } - } + parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], + }, + }, + }; - cascadedScan.metadata.name = cascadedScan.metadata.generateName + cascadedScan.metadata.name = cascadedScan.metadata.generateName; const secondCascadedScans = getCascadingScans( cascadedScan, findings, sslyzeCascadingRules, - sslyzeCascadingRules[0] // cascaded rule on parent + sslyzeCascadingRules[0], // cascaded rule on parent + parseDefinition ); const secondCascadedScan = secondCascadedScans[0]; - expect(secondCascadedScan.spec.env).toMatchInlineSnapshot(`Array []`) - - expect(secondCascadedScan.spec.volumes).toMatchInlineSnapshot(`Array []`) + expect(secondCascadedScan.spec.env).toMatchInlineSnapshot(`Array []`); - expect(secondCascadedScan.spec.volumeMounts).toMatchInlineSnapshot(`Array []`) + expect(secondCascadedScan.spec.volumes).toMatchInlineSnapshot(`Array []`); + expect(secondCascadedScan.spec.volumeMounts).toMatchInlineSnapshot( + `Array []` + ); }); - test("should append cascading rule to further cascading scan chains", () => { const findings = [ { @@ -2476,24 +2577,26 @@ test("should append cascading rule to further cascading scan chains", () => { state: "open", hostname: "foobar.com", port: 443, - service: "https" - } - } + service: "https", + }, + }, ]; const cascadedScans = getCascadingScans( parentScan, findings, - sslyzeCascadingRules + sslyzeCascadingRules, + undefined, + parseDefinition ); - const cascadedScan = cascadedScans[0] + const cascadedScan = cascadedScans[0]; // Create a second cascading rule sslyzeCascadingRules[1] = { apiVersion: "cascading.securecodebox.io/v1", kind: "CascadingRule", metadata: { - name: "tls-scans-second" + name: "tls-scans-second", }, spec: { matches: { @@ -2502,34 +2605,194 @@ test("should append cascading rule to further cascading scan chains", () => { category: "Open Port", attributes: { port: 443, - service: "https" - } + service: "https", + }, }, { category: "Open Port", attributes: { - service: "https" - } - } - ] + service: "https", + }, + }, + ], }, scanSpec: { scanType: "sslyze", - parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"] - } - } - } + parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"], + }, + }, + }; - cascadedScan.metadata.name = cascadedScan.metadata.generateName + cascadedScan.metadata.name = cascadedScan.metadata.generateName; const secondCascadedScans = getCascadingScans( cascadedScan, findings, sslyzeCascadingRules, - sslyzeCascadingRules[0] // cascaded rule on parent + sslyzeCascadingRules[0], // cascaded rule on parent + parseDefinition ); const secondCascadedScan = secondCascadedScans[0]; - expect(secondCascadedScan.metadata.annotations["cascading.securecodebox.io/chain"]).toEqual("tls-scans,tls-scans-second") + expect( + secondCascadedScan.metadata.annotations["cascading.securecodebox.io/chain"] + ).toEqual("tls-scans,tls-scans-second"); +}); + +test("should not cascade if scope limiter does not pass", () => { + parentScan.metadata.annotations["scope.cascading.securecodebox.io/ports"] = + "80,443"; + parentScan.spec.cascades.scopeLimiter = { + allOf: [ + { + key: "scope.cascading.securecodebox.io/ports", + operator: ScopeLimiterRequirementOperator.Contains, + values: ["{{$.port}}"], + }, + ], + }; + + const findings = [ + { + name: "Port 443 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 443, + service: "https", + }, + }, + { + name: "Port 8080 is open", + category: "Open Port", + attributes: { + state: "open", + hostname: "foobar.com", + port: 8080, + service: "https", + }, + }, + ]; + + parseDefinition.spec.scopeLimiterAliases["port"] = "{{attributes.port}}"; + + const cascadedScans = getCascadingScans( + parentScan, + findings, + sslyzeCascadingRules, + undefined, + parseDefinition + ); + + expect(cascadedScans).toMatchInlineSnapshot(` + Array [ + Object { + "apiVersion": "execution.securecodebox.io/v1", + "kind": "Scan", + "metadata": Object { + "annotations": Object { + "cascading.securecodebox.io/chain": "tls-scans", + "cascading.securecodebox.io/matched-finding": undefined, + "cascading.securecodebox.io/parent-scan": "nmap-foobar.com", + "scope.cascading.securecodebox.io/ports": "80,443", + "securecodebox.io/hook": "cascading-scans", + }, + "generateName": "sslyze-foobar.com-tls-scans-", + "labels": Object {}, + "ownerReferences": Array [ + Object { + "apiVersion": "execution.securecodebox.io/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "Scan", + "name": "nmap-foobar.com", + "uid": undefined, + }, + ], + }, + "spec": Object { + "affinity": undefined, + "cascades": Object { + "scopeLimiter": Object { + "allOf": Array [ + Object { + "key": "scope.cascading.securecodebox.io/ports", + "operator": "Contains", + "values": Array [ + "{{$.port}}", + ], + }, + ], + }, + }, + "env": Array [], + "hookSelector": Object {}, + "initContainers": Array [], + "parameters": Array [ + "--regular", + "foobar.com:443", + ], + "scanType": "sslyze", + "tolerations": undefined, + "volumeMounts": Array [], + "volumes": Array [], + }, + }, + ] + `); +}); + +test("scope annotations should be completely immutable", () => { + parentScan.metadata.annotations["scope.cascading.securecodebox.io/domains"] = + "example.com"; + parentScan.metadata.annotations["not.a.scope.annotation"] = "really"; + parentScan.spec.cascades.inheritAnnotations = false; + sslyzeCascadingRules[0].spec.scanAnnotations = { + "scope.cascading.securecodebox.io/domains": "malicious.example.com", + "another.not.a.scope.annotation": "really", + }; + 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, + undefined, + parseDefinition + ); + + expect(cascadedScans).toThrowError( + "may not add scope annotation 'scope.cascading.securecodebox.io/domains':'malicious.example.com' in Cascading Rule spec" + ); + + delete sslyzeCascadingRules[0].spec.scanAnnotations[ + "scope.cascading.securecodebox.io/domains" + ]; + + const cascadedScan = cascadedScans()[0]; + + expect(cascadedScan.metadata.annotations).toMatchInlineSnapshot(` + Object { + "another.not.a.scope.annotation": "really", + "cascading.securecodebox.io/chain": "tls-scans", + "cascading.securecodebox.io/matched-finding": undefined, + "cascading.securecodebox.io/parent-scan": "nmap-foobar.com", + "scope.cascading.securecodebox.io/domains": "example.com", + "securecodebox.io/hook": "cascading-scans", + } + `); }); diff --git a/hooks/cascading-scans/hook/hook.ts b/hooks/cascading-scans/hook/hook.ts index 307429812d..71005cdece 100644 --- a/hooks/cascading-scans/hook/hook.ts +++ b/hooks/cascading-scans/hook/hook.ts @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -import { isMatch, isMatchWith, isString, mapValues, cloneDeep } from "lodash"; +import { isMatch, isMatchWith, isString, mapValues, cloneDeep, pickBy, forEach } from "lodash"; import { isMatch as wildcardIsMatch } from "matcher"; import * as Mustache from "mustache"; @@ -13,12 +13,18 @@ import { Scan, Finding, CascadingRule, + ParseDefinition, getCascadedRuleForScan, + getParseDefinitionForScan, purgeCascadedRuleFromScan, mergeInheritedMap, mergeInheritedArray, mergeInheritedSelector, } from "./scan-helpers"; +import { + isInScope, + scopeDomain, +} from "./scope-limiter"; interface HandleArgs { scan: Scan; @@ -29,8 +35,9 @@ export async function handle({ scan, getFindings }: HandleArgs) { const findings = await getFindings(); const cascadingRules = await getCascadingRules(scan); const cascadedRuleUsedForParentScan = await getCascadedRuleForScan(scan); + const parseDefinition = await getParseDefinition(scan); - const cascadingScans = getCascadingScans(scan, findings, cascadingRules, cascadedRuleUsedForParentScan); + const cascadingScans = getCascadingScans(scan, findings, cascadingRules, cascadedRuleUsedForParentScan, parseDefinition); for (const cascadingScan of cascadingScans) { await startSubsequentSecureCodeBoxScan(cascadingScan); @@ -42,6 +49,11 @@ async function getCascadingRules(scan: Scan): Promise> { return >await getCascadingRulesForScan(scan); } +async function getParseDefinition(scan: Scan): Promise { + // Explicit Cast to the proper Type + return await getParseDefinitionForScan(scan); +} + /** * Goes thought the Findings and the CascadingRules * and returns a List of Scans which should be started based on both. @@ -50,7 +62,8 @@ export function getCascadingScans( parentScan: Scan, findings: Array, cascadingRules: Array, - cascadedRuleUsedForParentScan: CascadingRule + cascadedRuleUsedForParentScan: CascadingRule, + parseDefinition: ParseDefinition, ): Array { let cascadingScans: Array = []; const cascadingRuleChain = getScanChain(parentScan); @@ -67,7 +80,14 @@ export function getCascadingScans( continue; } - cascadingScans = cascadingScans.concat(getScansMatchingRule(parentScan, findings, cascadingRule)) + // Check for new scope annotations + forEach(cascadingRule.spec.scanAnnotations, (value, key) => { + if (key.startsWith(scopeDomain)) { + throw new Error(`may not add scope annotation '${key}':'${value}' in Cascading Rule spec`) + } + }); + + cascadingScans = cascadingScans.concat(getScansMatchingRule(parentScan, findings, cascadingRule, parseDefinition)) } return cascadingScans; @@ -86,9 +106,31 @@ export function getScanChain(parentScan: Scan) { return [] } -function getScansMatchingRule(parentScan: Scan, findings: Array, cascadingRule: CascadingRule) { +function getScansMatchingRule( + parentScan: Scan, + findings: Array, + cascadingRule: CascadingRule, + parseDefinition: ParseDefinition, +) { const cascadingScans: Array = []; for (const finding of findings) { + // Check if the scan matches for the current finding + const inScope = isInScope( + parentScan.spec.cascades.scopeLimiter, + parentScan.metadata.annotations, + finding, + parseDefinition.spec.scopeLimiterAliases, + ); + + if (!inScope) { + console.log(`Cascading Rule ${cascadingRule.metadata.name} not triggered as scope limiter did not pass`); + console.log(`Scan annotations ${JSON.stringify(parentScan.metadata.annotations)}`); + console.log(`Scope limiter ${JSON.stringify(parentScan.spec.cascades.scopeLimiter)}`); + console.log(`Scope limiter aliases ${JSON.stringify(parseDefinition.spec.scopeLimiterAliases)}`); + console.log(`Finding ${JSON.stringify(finding)}`); + continue; + } + // Check if one (ore more) of the CascadingRule matchers apply to the finding const matches = cascadingRule.spec.matches.anyOf.some(matchesRule => isMatch(finding, matchesRule) || isMatchWith(finding, matchesRule, wildcardMatcher) @@ -130,6 +172,7 @@ function getCascadingScan( ...cascadingChain, cascadingRule.metadata.name ].join(","), + ...pickBy(parentScan.metadata.annotations, (value, key) => key.startsWith(scopeDomain)), }, ownerReferences: [ { diff --git a/hooks/cascading-scans/hook/package-lock.json b/hooks/cascading-scans/hook/package-lock.json index adb5aefed8..a252220ac7 100644 --- a/hooks/cascading-scans/hook/package-lock.json +++ b/hooks/cascading-scans/hook/package-lock.json @@ -10,11 +10,14 @@ "license": "Apache-2.0", "dependencies": { "@kubernetes/client-node": "^0.15.0", + "ip-address": "^8.1.0", "lodash": "^4.17.21", "matcher": "^4.0.0", - "mustache": "^4.2.0" + "mustache": "^4.2.0", + "parse-domain": "^4.1.0" }, "devDependencies": { + "@types/ip-address": "^7.0.0", "@types/lodash": "^4.14.171", "@types/node": "^14.17.5", "jest": "^27.0.6", @@ -1074,6 +1077,16 @@ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, + "node_modules/@types/ip-address": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/ip-address/-/ip-address-7.0.0.tgz", + "integrity": "sha512-OyDm4EwZsYPDUjXz3ktiuQE8PJIPO1uUZMfvZMcWmykWjm3WVyI78rAnHkqKV3pMR7iDRKfalI+RxG5JBDUo5w==", + "deprecated": "This is a stub types definition. ip-address provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "ip-address": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -2559,6 +2572,36 @@ "node": ">= 0.10" } }, + "node_modules/ip-address": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-8.1.0.tgz", + "integrity": "sha512-Wz91gZKpNKoXtqvY8ScarKYwhXoK4r/b5QuT+uywe/azv0/nUCo7Bh0IRRI7F9DHR06kJNWtzMGLIbXavngbKA==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "1.1.2" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-ci": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", @@ -2600,6 +2643,17 @@ "node": ">=6" } }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3754,6 +3808,36 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -3951,6 +4035,19 @@ "node": ">=6" } }, + "node_modules/parse-domain": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-4.1.0.tgz", + "integrity": "sha512-zas79foMEsbMbIcJoPx26+NISWa3jTzZykOW9mXfRzvgadHvAHGd7qcCc1FbSWbD1I4qP71UWAxlTgu7Uq6IQg==", + "dependencies": { + "is-ip": "^3.1.0", + "node-fetch": "^2.6.1", + "punycode": "^2.1.1" + }, + "bin": { + "parse-domain-update": "bin/update.js" + } + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -5816,6 +5913,15 @@ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" }, + "@types/ip-address": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/ip-address/-/ip-address-7.0.0.tgz", + "integrity": "sha512-OyDm4EwZsYPDUjXz3ktiuQE8PJIPO1uUZMfvZMcWmykWjm3WVyI78rAnHkqKV3pMR7iDRKfalI+RxG5JBDUo5w==", + "dev": true, + "requires": { + "ip-address": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -6967,6 +7073,32 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, + "ip-address": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-8.1.0.tgz", + "integrity": "sha512-Wz91gZKpNKoXtqvY8ScarKYwhXoK4r/b5QuT+uywe/azv0/nUCo7Bh0IRRI7F9DHR06kJNWtzMGLIbXavngbKA==", + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "1.1.2" + }, + "dependencies": { + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "is-ci": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz", @@ -6996,6 +7128,14 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "requires": { + "ip-regex": "^4.0.0" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7886,6 +8026,35 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8023,6 +8192,16 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parse-domain": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-4.1.0.tgz", + "integrity": "sha512-zas79foMEsbMbIcJoPx26+NISWa3jTzZykOW9mXfRzvgadHvAHGd7qcCc1FbSWbD1I4qP71UWAxlTgu7Uq6IQg==", + "requires": { + "is-ip": "^3.1.0", + "node-fetch": "^2.6.1", + "punycode": "^2.1.1" + } + }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", diff --git a/hooks/cascading-scans/hook/package.json b/hooks/cascading-scans/hook/package.json index 438a417cda..0561bd68e4 100644 --- a/hooks/cascading-scans/hook/package.json +++ b/hooks/cascading-scans/hook/package.json @@ -9,7 +9,7 @@ }, "main": "hook.js", "scripts": { - "build": "npx tsc hook.ts --sourceMap", + "build": "npx tsc hook.ts scope-limiter.ts --sourceMap --esModuleInterop", "test": "jest . --verbose false" }, "keywords": [ @@ -38,11 +38,14 @@ "license": "Apache-2.0", "dependencies": { "@kubernetes/client-node": "^0.15.0", + "ip-address": "^8.1.0", "lodash": "^4.17.21", "matcher": "^4.0.0", - "mustache": "^4.2.0" + "mustache": "^4.2.0", + "parse-domain": "^4.1.0" }, "devDependencies": { + "@types/ip-address": "^7.0.0", "@types/lodash": "^4.14.171", "@types/node": "^14.17.5", "jest": "^27.0.6", diff --git a/hooks/cascading-scans/hook/scan-helpers.ts b/hooks/cascading-scans/hook/scan-helpers.ts index ac204a0dc1..e82d52dfb8 100644 --- a/hooks/cascading-scans/hook/scan-helpers.ts +++ b/hooks/cascading-scans/hook/scan-helpers.ts @@ -10,6 +10,7 @@ import { } from "./kubernetes-label-selector"; import {isEqual} from "lodash"; import {getScanChain} from "./hook"; +import {ScopeLimiterRequirement} from "./scope-limiter"; // configure k8s client const kc = new k8s.KubeConfig(); @@ -52,6 +53,7 @@ export interface Matches { export interface Scan { metadata: k8s.V1ObjectMeta; spec: ScanSpec; + status?: ScanStatus; } export interface ScanSpec { @@ -67,7 +69,15 @@ export interface ScanSpec { affinity?: k8s.V1Toleration; } +export interface ScopeLimiter { + validOnMissingRender: boolean, + anyOf?: Array, + allOf?: Array, + noneOf?: Array, +} + export interface CascadingInheritance { + scopeLimiter: ScopeLimiter, inheritLabels: boolean, inheritAnnotations: boolean, inheritEnv: boolean, @@ -78,6 +88,21 @@ export interface CascadingInheritance { inheritTolerations: boolean, } +export interface ScanStatus { + rawResultType: string, +} + +export interface ParseDefinition { + metadata: k8s.V1ObjectMeta; + spec: ParseDefinitionSpec; +} + +export interface ParseDefinitionSpec { + scopeLimiterAliases: ScopeLimiterAliases, +} + +export type ScopeLimiterAliases = { [key: string]: string; }; + export function mergeInheritedMap(parentProps, ruleProps, inherit: boolean = true) { if (!inherit) { parentProps = {}; @@ -161,6 +186,24 @@ export async function getCascadingRulesForScan(scan: Scan) { } } +export async function getParseDefinitionForScan(scan: Scan) { + try { + const response: any = await k8sApiCRD.getNamespacedCustomObject( + "execution.securecodebox.io", + "v1", + namespace, + "parsedefinitions", + scan.status.rawResultType, + ); + + return response.body; + } catch (err) { + console.error(`Failed to get ParseDefinition ${scan.status.rawResultType} from the kubernetes api`); + console.error(err); + process.exit(1); + } +} + // To ensure that the environment variables and volumes from the cascading rule are only applied to the matched scan // (and not its children), this function purges the cascading rule spec from the parent scan when inheriting them. export function purgeCascadedRuleFromScan(scan: Scan, cascadedRuleUsedForParentScan?: CascadingRule) : Scan { diff --git a/hooks/cascading-scans/hook/scope-limiter.test.js b/hooks/cascading-scans/hook/scope-limiter.test.js new file mode 100644 index 0000000000..659f0c8e26 --- /dev/null +++ b/hooks/cascading-scans/hook/scope-limiter.test.js @@ -0,0 +1,1007 @@ +// SPDX-FileCopyrightText: 2021 iteratec GmbH +// +// SPDX-License-Identifier: Apache-2.0 + +const { isInScope: isInScopeInternal } = require("./scope-limiter"); + +let scopeLimiter = undefined +let annotations = undefined +let finding = undefined +let scopeLimiterAliases = undefined + +const isInScope = () => isInScopeInternal( + scopeLimiter, + annotations, + finding, + scopeLimiterAliases +) + +beforeEach(function () { + scopeLimiter = { + validOnMissingRender: false, + allOf: [], + anyOf: [], + noneOf: [], + } + + annotations = {} + + finding = { + attributes: { + hostname: "example.com", + } + } + + scopeLimiterAliases = {} +}) + +it("Requirement key must start with 'scope.cascading.securecodebox.io/'", () => { + scopeLimiter.allOf = [ + { + key: "engagement.scope/domains", + operator: "Contains", + values: ["{{attributes.hostname}}"], + } + ] + expect(isInScope).toThrowError("key 'engagement.scope/domains' is invalid: key does not start with 'scope.cascading.securecodebox.io/'"); +}); + +it("Requirement key must map to an annotation", () => { + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "In", + values: ["{{attributes.hostname}}"], + } + ] + + finding = { + attributes: { + hostname: "notexample.com", + } + }; + + expect(isInScope).toThrowError("using operator 'In': the referenced annotation may not be undefined"); +}); + +describe("Templating", function () { + it("fails if the values are undefined", () => { + annotations = { + "scope.cascading.securecodebox.io/example.com": "example.com" + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/{{attributes.hostname}}", + operator: "Contains", + } + ] + expect(isInScope).toThrowError("the values field may not be undefined"); + }); + + it("does not support requirement key", () => { + annotations = { + "scope.cascading.securecodebox.io/example.com": "example.com" + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/{{attributes.hostname}}", + operator: "Contains", + values: ["{{attributes.hostname}}"], + } + ] + expect(isInScope).toThrowError("using operator 'Contains': the referenced annotation may not be undefined"); + }); + + it("supports requirement value", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com,subdomain.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["{{attributes.hostname}}"], + } + ] + expect(isInScope()).toBe(true); + }); + + describe("validOnMissingRender", function () { + it("does not match if mapping is not available: validOnMissingRender false", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com,subdomain.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["{{$.hostname}}"], + } + ] + + finding = {} + + expect(isInScope()).toBe(false); + }); + }) + + describe("aliases", function () { + it("matches using templates populated with finding and a mapped selector", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com,subdomain.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["{{$.hostname}}"], + } + ] + scopeLimiterAliases = { + "hostname": "{{attributes.hostname}}", + } + expect(isInScope()).toBe(true); + }); + + it("templates custom list functions in aliases", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com,subdomain.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["{{{$.hostname}}}"], + } + ] + finding = { + attributes: { + hostname: ["notexample.com", "example.com"], + } + } + scopeLimiterAliases = { + "hostname": "{{#asList}}attributes.hostname{{/asList}}", + } + expect(isInScope()).toBe(false); + }); + + it("Matches if mapping is not available: validOnMissingRender true", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com,subdomain.example.com", + } + + scopeLimiter.validOnMissingRender = true + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["{{$.hostname}}"], + } + ] + finding = {} + + expect(isInScope()).toBe(true); + }); + + }) + + describe("lists", function () { + describe("asList", function () { + it("matches with list of strings", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["{{#asList}}attributes.domains{{/asList}}"], + } + ] + + finding = { + attributes: { + domains: ["example.com", "subdomain.example.com"], + } + }; + + expect(isInScope()).toBe(true); + }); + + it("fails with too short key", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#asList}}attributes{{/asList}}"], + } + ] + + finding = {} + + expect(isInScope).toThrowError("Invalid list key 'attributes'. List key must be at least 2 levels deep. E.g. 'attributes.addresses'"); + }); + }) + + describe("split", function () { + it("matches on simple string", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}subdomain.example.com,www.example.com{{/split}}"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("matches on template", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}{{attributes.hostnames}}{{/split}}"], + } + ] + + finding = { + attributes: { + hostnames: ["subdomain.example.com", "www.example.com"], + } + } + + expect(isInScope()).toBe(true) + }); + + it("does not ignore the last entry in the split list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com,some.otherdomain.com{{/split}}"], + } + ] + + expect(isInScope()).toBe(false) + }); + + it("works for longer lists", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com,some.example.com,some-other.example.com,yet-another.example.com{{/split}}"], + } + ] + + expect(isInScope()).toBe(true) + }); + + it("also considers entries far back in the list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com,some.example.com,some-other.example.com,but.anotherwebsite.aswell.com{{/split}}"], + } + ] + + expect(isInScope()).toBe(false) + }); + + it("can handle spaces between entries", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com, some.example.com, some-other.example.com, yet-another.example.com{{/split}}"], + } + ] + + expect(isInScope()).toBe(true) + }); + + it("does not create extra empty entry for trailing comma (matching limiter)", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com,test.example.com,{{/split}}"], + } + ] + + expect(isInScope()).toBe(true) + }); + + it("does not create extra empty entry for trailing comma (non-matching limiter)", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}example.com,notexample.com,{{/split}}"], + } + ] + + expect(isInScope()).toBe(false) + scopeLimiter.validOnMissingRender = true + expect(isInScope()).toBe(false) + }); + + it("does not create extra empty entry for trailing comma (non-matching limiter from template)", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}{{#attributes.addresses}}{{domain}},{{/attributes.addresses}}{{/split}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "domain": "example.com" + }, + { + "domain": "notexample.com" + } + ] + } + }; + + // Fails because 'notexample.com' is not a subdomain of example.com + expect(isInScope()).toBe(false) + scopeLimiter.validOnMissingRender = true + expect(isInScope()).toBe(false) + }); + + it("respects validOnMissingRender", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["{{#split}}{{#attributes.addresses}}{{domain}},{{/attributes.addresses}}{{/split}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "domain": "example.com" + }, + { + "typo": "www.example.com" + } + ] + } + }; + + // Fails because 'typo' wasn't templated + expect(isInScope()).toBe(false) + scopeLimiter.validOnMissingRender = true + expect(isInScope()).toBe(true) + }); + }); + + describe("getValues", function () { + it("matches if templating key is present in all list entries", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#getValues}}attributes.addresses.ip{{/getValues}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "ip": "127.0.0.1" + }, + { + "ip": "fe80::4eb3:e128:53cc:5722" + } + ] + } + }; + + expect(isInScope()).toBe(true); + }); + + it("does not match if list with invalid keys", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#getValues}}attributes.randomkey.ip{{/getValues}}"], + } + ] + + finding = {} + + expect(isInScope()).toBe(false); + }); + + it("does not match if templating key is not present in all list entries", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#getValues}}attributes.addresses.ip{{/getValues}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "ip": "127.0.0.1" + }, + { + "ip": "fe80::4eb3:e128:53cc:5722" + }, + { + "other_key": "test" + } + ] + } + }; + + expect(isInScope()).toBe(false); + }); + + it("throws when the provided key is not at least 3 levels deep", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#getValues}}attributes.addresses{{/getValues}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "ip": "127.0.0.1" + }, + { + "ip": "fe80::4eb3:e128:53cc:5722" + }, + ] + } + }; + + expect(isInScope).toThrowError("Invalid list key 'attributes.addresses'. List key must be at least 3 levels deep. E.g. 'attributes.addresses.ip'"); + }); + + it("matches if validOnMissingRender is set and templating key is not present in all list entries", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR": "127.0.0.0/8", + } + scopeLimiter.validOnMissingRender = true + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR", + operator: "InCIDR", + values: ["{{#getValues}}attributes.addresses.ip{{/getValues}}"], + } + ] + + finding = { + attributes: { + addresses: [ + { + "ip": "127.0.0.1" + }, + { + "ip": "fe80::4eb3:e128:53cc:5722" + }, + { + "other_key": "test" + } + ] + } + }; + + expect(isInScope()).toBe(true); + }); + }) + + }) +}) + +describe("Operator", function () { + describe("In", function () { + it("matches if annotation is in value list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "In", + values: ["subdomain.example.com", "www.example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("does not match if annotation is not in value list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "In", + values: ["subdomain.example.com", "example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + }) + + describe("Contains", function () { + it("matches if value is in annotation list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "subdomain.example.com,www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["subdomain.example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("does not match if value is not in annotation list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "subdomain.example.com,www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("does not match if one of the values is not in annotation list", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "subdomain.example.com,www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["subdomain.example.com","example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + }); + + describe("InCIDR", function () { + it("matches if ip in subnet", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "10.0.0.0/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["10.0.1.0"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("does not match if ip not in subnet", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "10.0.0.0/32", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["10.0.1.0"], + } + ] + + expect(isInScope()).toBe(false); + }); + + it("matches if ip in subnet (IPv6)", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["2001:0:ce49:7601:e866:efff:62c3:ffff"], + } + ] + + expect(isInScope()).toBe(true); + }); + + it("matches if there is an IPv4/6 mismatch", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["10.0.1.0"], + } + ] + + expect(isInScope()).toBe(true); + }); + + it("does not match if there is an IPv4/6 mismatch AND an out-of-scope IPv4/6 match", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR4": "127.0.0.0/8", + "scope.cascading.securecodebox.io/CIDR6": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR4", + operator: "InCIDR", + values: ["192.168.178.42"], + }, + { + key: "scope.cascading.securecodebox.io/CIDR6", + operator: "InCIDR", + values: ["192.168.178.42"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("does not match if there exist out-of-scope matched IPv4/6 entries", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR4": "127.0.0.0/8", + "scope.cascading.securecodebox.io/CIDR6": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR4", + operator: "InCIDR", + values: ["192.168.178.42", "2001:0:ce49:7601:e866:efff:62c3:fefe"], + }, + { + key: "scope.cascading.securecodebox.io/CIDR6", + operator: "InCIDR", + values: ["192.168.178.42", "2001:0:ce49:7601:e866:efff:62c3:fefe"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("matches if there exist only in-scope matched IPv4/6 entries", () => { + annotations = { + "scope.cascading.securecodebox.io/CIDR4": "127.0.0.0/8", + "scope.cascading.securecodebox.io/CIDR6": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/CIDR4", + operator: "InCIDR", + values: ["127.0.0.5", "2001:0:ce49:7601:e866:efff:62c3:fefe"], + }, + { + key: "scope.cascading.securecodebox.io/CIDR6", + operator: "InCIDR", + values: ["127.0.0.5", "2001:0:ce49:7601:e866:efff:62c3:fefe"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("throws error if IPv4 address is invalid even if scope is in IPv6", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["10.0.0.257"], // Invalid IPv4 + } + ] + + expect(isInScope).toThrowError("Bad characters detected in address: .."); + }); + + it("Throws error if IPv6 address is invalid even if scope is in IPv4", () => { + annotations = { + "scope.cascading.securecodebox.io/cidr": "10.0.0.0/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/cidr", + operator: "InCIDR", + values: ["2001:0:ce49:7601:e866:efff:62c3"], + } + ] + + expect(isInScope).toThrowError("Incorrect number of groups found"); + }); + }); + + describe("SubdomainOf", function () { + it("does not match if annotation domain is not a domain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "I am not a domain", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["subdomain.example.com"], + } + ] + expect(isInScope).toThrowError("I am not a domain is an invalid domain name"); + }); + + it("does not match if finding domain is not a domain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["I am not a domain"], + } + ] + expect(isInScope).toThrowError("I am not a domain is an invalid domain name"); + }); + + it("does not match if target domain is deeper nested than the finding domain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "some.subdomain.of.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("matches if is subdomain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["subdomain.example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("does not match if is not subdomain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["notexample.com"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("matches if is the domain itself", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("matches if providing a sub-sub domain of a sub-domain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "www.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["test.www.example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("matches if providing a deep subdomain of a deep subdomain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "a.b.c.d.e.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["z.a.b.c.d.e.example.com"], + } + ] + expect(isInScope()).toBe(true); + }); + + it("does not match even if differences are deep in the subdomain tree", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "a.b.c.d.e.example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["z.b.c.d.e.example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("does not match if providing a sub domain of a different sub-domain", () => { + annotations = { + "scope.cascading.securecodebox.io/domain": "www.example.com", + } + scopeLimiter.anyOf = [ + { + key: "scope.cascading.securecodebox.io/domain", + operator: "SubdomainOf", + values: ["test.example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + }); +}) + +describe("ScopeLimiter", function () { + it("does not match if one of selector types does not match", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["example.com"], + } + ] + scopeLimiter.noneOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "Contains", + values: ["example.com"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("ANDs together results from multiple different limiter classes and fails if one fails", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + "scope.cascading.securecodebox.io/cidr4": "8.8.8.8/8", + "scope.cascading.securecodebox.io/cidr6": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["subdomain.example.com"], + } + ] + scopeLimiter.anyOf = [ + { + key: "scope.cascading.securecodebox.io/cidr4", + operator: "InCIDR", + values: ["127.0.0.1", "fe80::1"], + }, + { + key: "scope.cascading.securecodebox.io/cidr6", + operator: "InCIDR", + values: ["127.0.0.1", "fe80::1"], + } + ] + expect(isInScope()).toBe(false); + }); + + it("ANDs together results from multiple different limiter classes", () => { + annotations = { + "scope.cascading.securecodebox.io/domains": "example.com", + "scope.cascading.securecodebox.io/cidr4": "127.0.0.1/8", + "scope.cascading.securecodebox.io/cidr6": "2001:0:ce49:7601:e866:efff:62c3:fffe/16", + } + scopeLimiter.allOf = [ + { + key: "scope.cascading.securecodebox.io/domains", + operator: "SubdomainOf", + values: ["subdomain.example.com"], + } + ] + scopeLimiter.anyOf = [ + { + key: "scope.cascading.securecodebox.io/cidr4", + operator: "InCIDR", + values: ["127.0.0.1", "fe80::1"], + }, + { + key: "scope.cascading.securecodebox.io/cidr6", + operator: "InCIDR", + values: ["127.0.0.1", "fe80::1"], + } + ] + expect(isInScope()).toBe(true); + }); +}) diff --git a/hooks/cascading-scans/hook/scope-limiter.ts b/hooks/cascading-scans/hook/scope-limiter.ts new file mode 100644 index 0000000000..25ab2526ac --- /dev/null +++ b/hooks/cascading-scans/hook/scope-limiter.ts @@ -0,0 +1,319 @@ +import { + Finding, + ScopeLimiter, + ScopeLimiterAliases, +} from "./scan-helpers"; +import { + V1ObjectMeta +} from "@kubernetes/client-node/dist/gen/model/v1ObjectMeta"; +import * as Mustache from "mustache"; +import { Address4, Address6 } from "ip-address"; +import { + fromUrl, + parseDomain, + ParseResultType +} from "parse-domain"; +import { + flatten, + isEqual, + takeRight +} from "lodash"; + +export enum ScopeLimiterRequirementOperator { + In = "In", + NotIn = "NotIn", + Contains = "Contains", + DoesNotContain = "DoesNotContain", + InCIDR = "InCIDR", + NotInCIDR = "NotInCIDR", + SubdomainOf = "SubdomainOf", + NotSubdomainOf = "NotSubdomainOf", +} + +export interface ScopeLimiterRequirement { + key: string; + operator: ScopeLimiterRequirementOperator; + values: Array; +} + +export const scopeDomain = "scope.cascading.securecodebox.io/" + +export function isInScope( + scopeLimiter: ScopeLimiter, + scanAnnotations: V1ObjectMeta['annotations'], + finding: Finding, + scopeLimiterAliases: ScopeLimiterAliases, +) { + if (scopeLimiter === undefined) return true; + + // Checks whether the key/operator/values pair successfully resolves + function validateRequirement({key, operator, values}: ScopeLimiterRequirement): boolean { + if (!key.startsWith(`${scopeDomain}`)) { + throw new Error(`key '${key}' is invalid: key does not start with '${scopeDomain}'`); + } + + // Retrieve operator and validator functions from user operator input + const { operator: operatorFunction, validator: validatorFunction } = operatorFunctions[operator]; + if (operatorFunction === undefined) { + throw new Error(`Unknown operator '${operator}'`); + } + const scopeAnnotationValue = scanAnnotations[key]; + + // Possible redundant check as the field is required by CRD + if (values === undefined) { + throw new Error("the values field may not be undefined") + } + + // Template the user values input using Mustache + const findingValues = values.map(templateValue); + // If one of the user values couldn't be rendered, fallback to user-defined behaviour + if (findingValues.some(render => !render.rendered)) { + return scopeLimiter.validOnMissingRender; + } + + const props: Operands = { + scopeAnnotationValue, + // flatten is the values to get rid of nested lists (caused by our custom Mustache list function) + findingValues: flatten(findingValues.map(render => render.values)) + }; + + try { + validatorFunction(props); + } catch (error) { + throw new Error(`using operator '${operator}': ${error.message}`); + } + + return operatorFunction(props); + } + + function templateValue(value: string): {values: string[], rendered: boolean} { + if (value === undefined) return { + values: [], + rendered: true, + }; + // First try to render scope limiter aliases + let mapped = Mustache.render(value, { + $: { + ...scopeLimiterAliases + } + }); + // If it couldn't be rendered as an alias, try render it again with finding + if (mapped == "") { + mapped = value; + } + const delimiter = ";;;;" + let rendered = Mustache.render(mapped, { + ...finding, + // These custom mustache functions all return a string containing a list delimited by `delimiter` defined above. + "getValues": function () { + // Select attributes inside a list of objects + return function (text, render) { + text = text.trim(); + const path = text.split("."); + if (path.length < 3) { + throw new Error(`Invalid list key '${text}'. List key must be at least 3 levels deep. E.g. 'attributes.addresses.ip'`) + } + const listKey = path.slice(0, path.length - 1).join("."); + const objectKey = path.pop(); + return render(`{{#${listKey}}}{{${objectKey}}}${delimiter}{{/${listKey}}}`); + } + }, + "asList": function () { + // Select a complete list + return function (text, render) { + text = text.trim(); + const path = text.split("."); + if (path.length < 2) { + throw new Error(`Invalid list key '${text}'. List key must be at least 2 levels deep. E.g. 'attributes.addresses'`) + } + return render(`{{#${text}}}{{.}}${delimiter}{{/${text}}}`); + } + }, + "split": function () { + // Split an existing list by comma + return function (text, render) { + // We are using a regular expression of the comma delimiter instead of a straight comma because + // NodeJS 14.X only replaces the first occurence when using the latter, and the + // replaceAll function is only available starting with NodeJS 15. + // First replace comma with trailing space in case the list is specified as "entry1, entry2". + // Then replace any leftover commas without a space, in case the list format is "entry1,entry2". + const result = render(text).trim().replace(/, /g, delimiter).replace(/,/g, delimiter); + if (result === "" || result.endsWith(delimiter)) { + return result; + } else { + return result.concat(delimiter) + } + } + }, + } + ); + // If the final render includes a delimiter, unpack the rendered string to an actual list + if (rendered.includes(delimiter)) { + let list = rendered.split(delimiter); + // The last element is always an empty string + list = list.slice(0, list.length - 1); + return { + values: list, + rendered: list.every(value => value != ""), + } + } else { + return { + values: [rendered], + rendered: rendered != "", + } + } + } + + // All the different scope limiter fields must match (i.e. results of `allOf`, `anyOf`, `noneOf` are ANDed). + // If one of those fields is not declared, regard it as matched. + return [ + scopeLimiter.allOf !== undefined && scopeLimiter.allOf.length > 0 ? scopeLimiter.allOf.every(validateRequirement) : true, + scopeLimiter.anyOf !== undefined && scopeLimiter.anyOf.length > 0 ? scopeLimiter.anyOf.some(validateRequirement) : true, + scopeLimiter.noneOf !== undefined && scopeLimiter.noneOf.length > 0 ? !scopeLimiter.noneOf.some(validateRequirement) : true, + ].every(entry => entry === true); +} + +interface Operands { + scopeAnnotationValue: string, + findingValues: string[], +} + +interface OperatorFunctions { + operator: (operands: Operands) => boolean, + validator: (operands: Operands) => void, +} + +// This validator ensures that neither the scope annotation nor the finding values can be undefined +const defaultValidator: OperatorFunctions["validator"] = props => validate(props, false); + +const operatorFunctions: { [key in ScopeLimiterRequirementOperator]: OperatorFunctions } = { + [ScopeLimiterRequirementOperator.In]: { + operator: operatorIn, + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.NotIn]: { + operator: props => !operatorIn(props), + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.Contains]: { + operator: operatorContains, + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.DoesNotContain]: { + operator: props => !operatorContains(props), + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.InCIDR]: { + operator: operatorInCIDR, + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.NotInCIDR]: { + operator: props => !operatorInCIDR(props), + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.SubdomainOf]: { + operator: operatorSubdomainOf, + validator: defaultValidator, + }, + [ScopeLimiterRequirementOperator.NotSubdomainOf]: { + operator: props => !operatorSubdomainOf(props), + validator: defaultValidator, + }, +} + +function validate({scopeAnnotationValue, findingValues}: Operands, scopeAnnotationValueUndefinedAllowed) { + if (!scopeAnnotationValueUndefinedAllowed && scopeAnnotationValue === undefined) { + throw new Error(`the referenced annotation may not be undefined`) + } +} + +/** + * The scope annotation value exists in one of the finding values. + * Matching example: + * scopeAnnotationValue: "example.com" + * findingValues: ["example.com", "subdomain.example.com"] + */ +function operatorIn({scopeAnnotationValue, findingValues}: Operands): boolean { + return findingValues.includes(scopeAnnotationValue); +} + +/** + * The scope annotation value is considered a comma-seperated list and checks if every finding value is in that list. + * Matching example: + * scopeAnnotationValue: "example.com,subdomain.example.com,other.example.com" + * findingValues: ["example.com", "subdomain.example.com"] + */ +function operatorContains({scopeAnnotationValue, findingValues}: Operands): boolean { + const scopeAnnotationValues = scopeAnnotationValue.split(","); + return findingValues.every(findingValue => scopeAnnotationValues.includes(findingValue)); +} + +/** + * The scope annotation value is considered CIDR and checks if every finding value is within the subnet of that CIDR. + * Supports both IPv4 and IPv6. If the scope is defined in IPv4, will only validate IPv4 IPs in the finding values. + * Vice-versa for IPv6 defined in scope and IPv4 found in values. Note that all IPs in finding values must be valid + * addresses, regardless of whether IPv4 or IPv6 was used in the scope definition. + * Matching example: + * scopeAnnotationValue: "10.10.0.0/16" + * findingValues: ["10.10.1.2", "10.10.1.3", "2001:0:ce49:7601:e866:efff:62c3:fffe"] + */ +function operatorInCIDR({scopeAnnotationValue, findingValues}: Operands): boolean { + + function getIPv4Or6(ipValue: string): Address4 | Address6 { + try { + return new Address4(ipValue); + } catch (e) { + if (e.name === "AddressError" && e.message === "Invalid IPv4 address.") { + try { + return new Address6(ipValue); + } catch (e) { + if (e.name === "AddressError" && e.message === "Invalid IPv6 address.") { + throw new Error(`${ipValue} is neither a IPv4 or IPv6`); + } else throw e; + } + } else throw e; + } + } + + let scopeAnnotationSubnet = getIPv4Or6(scopeAnnotationValue); + + + return findingValues.every(findingValue => { + const address = getIPv4Or6(findingValue); + // Can't compare IPv4 with IPv6, so we return regard such comparison as true + if (address.constructor !== scopeAnnotationSubnet.constructor) return true; + + return address.isInSubnet(scopeAnnotationSubnet); + }); +} + +/** + * Checks if every finding value is a subdomain of the scope annotation value. + * Inclusive; i.e. example.com is a subdomain of example.com. + * Matching example: + * scopeAnnotationValue: "example.com" + * findingValues: ["subdomain.example.com", "example.com"] + */ +function operatorSubdomainOf({scopeAnnotationValue, findingValues}: Operands): boolean { + const scopeAnnotationDomain = parseDomain(fromUrl(scopeAnnotationValue)); + if (scopeAnnotationDomain.type == ParseResultType.Listed) { + return findingValues.every(findingValue => { + const findingDomain = parseDomain(fromUrl(findingValue)); + if (findingDomain.type == ParseResultType.Listed) { + // Equal length domains can pass as subdomain of + if (scopeAnnotationDomain.labels.length > findingDomain.labels.length) { + return false; + } + + // Check if last part of domain is equal + return isEqual( + scopeAnnotationDomain.labels, + takeRight(findingDomain.labels, scopeAnnotationDomain.labels.length) + ); + } + throw new Error(`${findingValue} is an invalid domain name`); + }) + } else { + throw new Error(`${scopeAnnotationValue} is an invalid domain name`); + } +} diff --git a/hooks/cascading-scans/templates/role.yaml b/hooks/cascading-scans/templates/role.yaml index acf2bd764d..dcd4f12c4a 100644 --- a/hooks/cascading-scans/templates/role.yaml +++ b/hooks/cascading-scans/templates/role.yaml @@ -17,6 +17,12 @@ rules: verbs: - get - create + - apiGroups: + - execution.securecodebox.io + resources: + - parsedefinitions + verbs: + - get - apiGroups: - execution.securecodebox.io resources: diff --git a/operator/apis/execution/v1/parsedefinition_types.go b/operator/apis/execution/v1/parsedefinition_types.go index d61f56320c..651bdaf0d6 100644 --- a/operator/apis/execution/v1/parsedefinition_types.go +++ b/operator/apis/execution/v1/parsedefinition_types.go @@ -17,6 +17,8 @@ type ParseDefinitionSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file + ScopeLimiterAliases map[string]string `json:"scopeLimiterAliases,omitempty"` + // Image is the reference to the parser container image which ca transform the raw scan report into findings Image string `json:"image,omitempty"` // ImagePullSecrets used to access private parser images diff --git a/operator/apis/execution/v1/scan_types.go b/operator/apis/execution/v1/scan_types.go index 8fd88aa5db..4ffb641ba7 100644 --- a/operator/apis/execution/v1/scan_types.go +++ b/operator/apis/execution/v1/scan_types.go @@ -14,6 +14,10 @@ import ( // 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 + ScopeLimiter ScopeLimiter `json:"scopeLimiter"` + // InheritLabels defines whether cascading scans should inherit labels from the parent scan // +optional // +kubebuilder:default=true @@ -64,6 +68,36 @@ type CascadeSpec struct { MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty" protobuf:"bytes,2,rep,name=matchExpressions"` } +type ScopeLimiter struct { + // ValidOnMissingRender defines whether if a templating variable is not present, that condition should match + // +optional + // +kubebuilder:default=false + ValidOnMissingRender bool `json:"validOnMissingRender"` + + // AnyOf is a list of label selector requirements. The requirements are ANDed. + // +optional + AnyOf []ScopeLimiterRequirement `json:"anyOf,omitempty" protobuf:"bytes,2,rep,name=anyOf"` + + // AllOf is a list of label selector requirements. The requirements are ANDed. + // +optional + AllOf []ScopeLimiterRequirement `json:"allOf,omitempty" protobuf:"bytes,2,rep,name=allOf"` + + // NoneOf is a list of label selector requirements. The requirements are ANDed. + // +optional + NoneOf []ScopeLimiterRequirement `json:"noneOf,omitempty" protobuf:"bytes,2,rep,name=noneOf"` +} + +// ScopeLimiterRequirement is a selector that contains values, a key, and an operator that +// relates the key and values. +type ScopeLimiterRequirement struct { + // key is the label key that the selector applies to. + Key string `json:"key" protobuf:"bytes,1,opt,name=key"` + // operator represents a key's relationship to a set of values. + Operator string `json:"operator" protobuf:"bytes,2,opt,name=operator"` + // values is an array of string values. + Values []string `json:"values" protobuf:"bytes,3,rep,name=values"` +} + // ScanSpec defines the desired state of Scan type ScanSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster diff --git a/operator/apis/execution/v1/zz_generated.deepcopy.go b/operator/apis/execution/v1/zz_generated.deepcopy.go index ac8959ecc7..c686b2dcd8 100644 --- a/operator/apis/execution/v1/zz_generated.deepcopy.go +++ b/operator/apis/execution/v1/zz_generated.deepcopy.go @@ -18,6 +18,7 @@ import ( // 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 + in.ScopeLimiter.DeepCopyInto(&out.ScopeLimiter) if in.MatchLabels != nil { in, out := &in.MatchLabels, &out.MatchLabels *out = make(map[string]string, len(*in)) @@ -174,6 +175,13 @@ func (in *ParseDefinitionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ParseDefinitionSpec) DeepCopyInto(out *ParseDefinitionSpec) { *out = *in + if in.ScopeLimiterAliases != nil { + in, out := &in.ScopeLimiterAliases, &out.ScopeLimiterAliases + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets *out = make([]corev1.LocalObjectReference, len(*in)) @@ -752,3 +760,59 @@ func (in *ScheduledScanStatus) DeepCopy() *ScheduledScanStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScopeLimiter) DeepCopyInto(out *ScopeLimiter) { + *out = *in + if in.AnyOf != nil { + in, out := &in.AnyOf, &out.AnyOf + *out = make([]ScopeLimiterRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AllOf != nil { + in, out := &in.AllOf, &out.AllOf + *out = make([]ScopeLimiterRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NoneOf != nil { + in, out := &in.NoneOf, &out.NoneOf + *out = make([]ScopeLimiterRequirement, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeLimiter. +func (in *ScopeLimiter) DeepCopy() *ScopeLimiter { + if in == nil { + return nil + } + out := new(ScopeLimiter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScopeLimiterRequirement) DeepCopyInto(out *ScopeLimiterRequirement) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeLimiterRequirement. +func (in *ScopeLimiterRequirement) DeepCopy() *ScopeLimiterRequirement { + if in == nil { + return nil + } + out := new(ScopeLimiterRequirement) + in.DeepCopyInto(out) + return out +} diff --git a/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml b/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml index 5299d44739..c6e60f4784 100644 --- a/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml +++ b/operator/config/crd/bases/cascading.securecodebox.io_cascadingrules.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 @@ -804,6 +801,98 @@ spec: the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans + should inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a + templating variable is not present, that condition should + match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/operator/config/crd/bases/execution.securecodebox.io_parsedefinitions.yaml b/operator/config/crd/bases/execution.securecodebox.io_parsedefinitions.yaml index 19d5e95cee..e1145c50d7 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_parsedefinitions.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_parsedefinitions.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 @@ -761,6 +758,10 @@ spec: type: string type: object type: array + scopeLimiterAliases: + additionalProperties: + type: string + type: object tolerations: description: Tolerations are a different way to control on which nodes your parser is executed. See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ diff --git a/operator/config/crd/bases/execution.securecodebox.io_scancompletionhooks.yaml b/operator/config/crd/bases/execution.securecodebox.io_scancompletionhooks.yaml index 6f27ae2885..896c54691c 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scancompletionhooks.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scancompletionhooks.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 diff --git a/operator/config/crd/bases/execution.securecodebox.io_scans.yaml b/operator/config/crd/bases/execution.securecodebox.io_scans.yaml index 0e87e594b0..01ae4e9712 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scans.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scans.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 @@ -739,6 +736,97 @@ spec: is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a templating + variable is not present, that condition should match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/operator/config/crd/bases/execution.securecodebox.io_scantypes.yaml b/operator/config/crd/bases/execution.securecodebox.io_scantypes.yaml index b152d06a11..a5457b382e 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scantypes.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scantypes.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 diff --git a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml index 633802c92b..7f58229ca2 100644 --- a/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/config/crd/bases/execution.securecodebox.io_scheduledscans.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 @@ -786,6 +783,98 @@ spec: the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans + should inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a + templating variable is not present, that condition should + match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/operator/crds/cascading.securecodebox.io_cascadingrules.yaml b/operator/crds/cascading.securecodebox.io_cascadingrules.yaml index 5299d44739..334ef50049 100644 --- a/operator/crds/cascading.securecodebox.io_cascadingrules.yaml +++ b/operator/crds/cascading.securecodebox.io_cascadingrules.yaml @@ -804,6 +804,98 @@ spec: the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans + should inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a + templating variable is not present, that condition should + match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/operator/crds/execution.securecodebox.io_parsedefinitions.yaml b/operator/crds/execution.securecodebox.io_parsedefinitions.yaml index 19d5e95cee..e1145c50d7 100644 --- a/operator/crds/execution.securecodebox.io_parsedefinitions.yaml +++ b/operator/crds/execution.securecodebox.io_parsedefinitions.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2021 iteratec GmbH -# -# SPDX-License-Identifier: Apache-2.0 --- apiVersion: apiextensions.k8s.io/v1 @@ -761,6 +758,10 @@ spec: type: string type: object type: array + scopeLimiterAliases: + additionalProperties: + type: string + type: object tolerations: description: Tolerations are a different way to control on which nodes your parser is executed. See https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ diff --git a/operator/crds/execution.securecodebox.io_scans.yaml b/operator/crds/execution.securecodebox.io_scans.yaml index 0e87e594b0..c27a0383ea 100644 --- a/operator/crds/execution.securecodebox.io_scans.yaml +++ b/operator/crds/execution.securecodebox.io_scans.yaml @@ -739,6 +739,97 @@ spec: is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans should + inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates the + key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a templating + variable is not present, that condition should match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/operator/crds/execution.securecodebox.io_scheduledscans.yaml b/operator/crds/execution.securecodebox.io_scheduledscans.yaml index 633802c92b..4b900fb472 100644 --- a/operator/crds/execution.securecodebox.io_scheduledscans.yaml +++ b/operator/crds/execution.securecodebox.io_scheduledscans.yaml @@ -786,6 +786,98 @@ spec: the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object + scopeLimiter: + description: InheritLabels defines whether cascading scans + should inherit labels from the parent scan + properties: + allOf: + description: AllOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + anyOf: + description: AnyOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + noneOf: + description: NoneOf is a list of label selector requirements. + The requirements are ANDed. + items: + description: ScopeLimiterRequirement is a selector that + contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. + type: string + values: + description: values is an array of string values. + items: + type: string + type: array + required: + - key + - operator + - values + type: object + type: array + validOnMissingRender: + default: false + description: ValidOnMissingRender defines whether if a + templating variable is not present, that condition should + match + type: boolean + type: object type: object env: description: Env allows to specify environment vars for the scanner diff --git a/scanners/amass/README.md b/scanners/amass/README.md index a0efd16123..0cc7f88fda 100644 --- a/scanners/amass/README.md +++ b/scanners/amass/README.md @@ -74,6 +74,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-amass"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/amass/templates/amass-parse-definition.yaml b/scanners/amass/templates/amass-parse-definition.yaml index 90ff3ef0ae..73d8d9e7cb 100644 --- a/scanners/amass/templates/amass-parse-definition.yaml +++ b/scanners/amass/templates/amass-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/amass/values.yaml b/scanners/amass/values.yaml index b84f89ce77..69bb54a012 100644 --- a/scanners/amass/values.yaml +++ b/scanners/amass/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/angularjs-csti-scanner/README.md b/scanners/angularjs-csti-scanner/README.md index 859dc8ca75..0b69684590 100644 --- a/scanners/angularjs-csti-scanner/README.md +++ b/scanners/angularjs-csti-scanner/README.md @@ -173,6 +173,7 @@ options.scope.request_methods = [ | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-angularjs-csti-scanner"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/angularjs-csti-scanner/templates/angularjs-csti-scanner-parse-definition.yaml b/scanners/angularjs-csti-scanner/templates/angularjs-csti-scanner-parse-definition.yaml index 6a6b126b62..f616decb4c 100644 --- a/scanners/angularjs-csti-scanner/templates/angularjs-csti-scanner-parse-definition.yaml +++ b/scanners/angularjs-csti-scanner/templates/angularjs-csti-scanner-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/angularjs-csti-scanner/values.yaml b/scanners/angularjs-csti-scanner/values.yaml index 6f9f15b997..fb43a38a54 100644 --- a/scanners/angularjs-csti-scanner/values.yaml +++ b/scanners/angularjs-csti-scanner/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/cmseek/templates/cmseek-parse-definition.yaml b/scanners/cmseek/templates/cmseek-parse-definition.yaml index 183b26cd4d..85635deed6 100644 --- a/scanners/cmseek/templates/cmseek-parse-definition.yaml +++ b/scanners/cmseek/templates/cmseek-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/cmseek/values.yaml b/scanners/cmseek/values.yaml index aa7bfc0cb9..2b12898b51 100644 --- a/scanners/cmseek/values.yaml +++ b/scanners/cmseek/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/git-repo-scanner/README.md b/scanners/git-repo-scanner/README.md index b0106e3757..cc87dbc23c 100644 --- a/scanners/git-repo-scanner/README.md +++ b/scanners/git-repo-scanner/README.md @@ -103,6 +103,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-git-repo-scanner"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/git-repo-scanner/templates/git-repo-scanner-parse-definition.yaml b/scanners/git-repo-scanner/templates/git-repo-scanner-parse-definition.yaml index f6a0d4ee29..5555a4cc58 100644 --- a/scanners/git-repo-scanner/templates/git-repo-scanner-parse-definition.yaml +++ b/scanners/git-repo-scanner/templates/git-repo-scanner-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/git-repo-scanner/values.yaml b/scanners/git-repo-scanner/values.yaml index f9a526f404..50cb204759 100644 --- a/scanners/git-repo-scanner/values.yaml +++ b/scanners/git-repo-scanner/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/gitleaks/README.md b/scanners/gitleaks/README.md index 607ad9b3d4..3b6221a60a 100644 --- a/scanners/gitleaks/README.md +++ b/scanners/gitleaks/README.md @@ -166,6 +166,7 @@ For more information on how to use cascades take a look at | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-gitleaks"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/gitleaks/templates/gitleaks-parse-definition.yaml b/scanners/gitleaks/templates/gitleaks-parse-definition.yaml index 10b8cde9f9..b4029108a4 100644 --- a/scanners/gitleaks/templates/gitleaks-parse-definition.yaml +++ b/scanners/gitleaks/templates/gitleaks-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/gitleaks/values.yaml b/scanners/gitleaks/values.yaml index 058a6c9764..00e5be0a21 100644 --- a/scanners/gitleaks/values.yaml +++ b/scanners/gitleaks/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/kube-hunter/README.md b/scanners/kube-hunter/README.md index 2a1cdba6fb..9bbf43bbe1 100644 --- a/scanners/kube-hunter/README.md +++ b/scanners/kube-hunter/README.md @@ -67,6 +67,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-kube-hunter"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/kube-hunter/templates/kube-hunter-parse-definition.yaml b/scanners/kube-hunter/templates/kube-hunter-parse-definition.yaml index fbd7fffdcd..c78c1a3268 100644 --- a/scanners/kube-hunter/templates/kube-hunter-parse-definition.yaml +++ b/scanners/kube-hunter/templates/kube-hunter-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/kube-hunter/values.yaml b/scanners/kube-hunter/values.yaml index 262b311ee9..ce377170a7 100644 --- a/scanners/kube-hunter/values.yaml +++ b/scanners/kube-hunter/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/kubeaudit/README.md b/scanners/kubeaudit/README.md index a44dd9b310..c0079e60f8 100644 --- a/scanners/kubeaudit/README.md +++ b/scanners/kubeaudit/README.md @@ -71,6 +71,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-kubeaudit"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/kubeaudit/templates/kubeaudit-parse-definition.yaml b/scanners/kubeaudit/templates/kubeaudit-parse-definition.yaml index 6c27c3c926..6fd7aa76a9 100644 --- a/scanners/kubeaudit/templates/kubeaudit-parse-definition.yaml +++ b/scanners/kubeaudit/templates/kubeaudit-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/kubeaudit/values.yaml b/scanners/kubeaudit/values.yaml index 20f671715c..8f3b619a2b 100644 --- a/scanners/kubeaudit/values.yaml +++ b/scanners/kubeaudit/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/ncrack/README.md b/scanners/ncrack/README.md index b7b2ea0b0a..56508890ab 100644 --- a/scanners/ncrack/README.md +++ b/scanners/ncrack/README.md @@ -215,6 +215,7 @@ helm delete ncrack | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-ncrack"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/ncrack/templates/ncrack-parse-definition.yaml b/scanners/ncrack/templates/ncrack-parse-definition.yaml index 6f1de86452..150e9f5185 100644 --- a/scanners/ncrack/templates/ncrack-parse-definition.yaml +++ b/scanners/ncrack/templates/ncrack-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/ncrack/values.yaml b/scanners/ncrack/values.yaml index 678a730352..d83485592c 100644 --- a/scanners/ncrack/values.yaml +++ b/scanners/ncrack/values.yaml @@ -23,6 +23,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/nikto/README.md b/scanners/nikto/README.md index 5cb3551118..daec720c08 100644 --- a/scanners/nikto/README.md +++ b/scanners/nikto/README.md @@ -86,6 +86,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-nikto"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/nikto/templates/nikto-parse-definition.yaml b/scanners/nikto/templates/nikto-parse-definition.yaml index 3a40e64fcd..9cbf15a066 100644 --- a/scanners/nikto/templates/nikto-parse-definition.yaml +++ b/scanners/nikto/templates/nikto-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/nikto/values.yaml b/scanners/nikto/values.yaml index 3d044a9d70..602e2f6af9 100644 --- a/scanners/nikto/values.yaml +++ b/scanners/nikto/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/nmap/README.md b/scanners/nmap/README.md index d7fcc5c48f..80d221b0ce 100644 --- a/scanners/nmap/README.md +++ b/scanners/nmap/README.md @@ -137,6 +137,7 @@ spec: | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-nmap"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/nmap/templates/nmap-parse-definition.yaml b/scanners/nmap/templates/nmap-parse-definition.yaml index 821df44060..9e5bc83e52 100644 --- a/scanners/nmap/templates/nmap-parse-definition.yaml +++ b/scanners/nmap/templates/nmap-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/nmap/values.yaml b/scanners/nmap/values.yaml index 802aff0163..597b882e73 100644 --- a/scanners/nmap/values.yaml +++ b/scanners/nmap/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/nuclei/README.md b/scanners/nuclei/README.md index 7c57180a1e..304f37fdf7 100644 --- a/scanners/nuclei/README.md +++ b/scanners/nuclei/README.md @@ -170,6 +170,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-nuclei"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/nuclei/templates/nuclei-parse-definition.yaml b/scanners/nuclei/templates/nuclei-parse-definition.yaml index ef96f49f86..b0a1f19fbb 100644 --- a/scanners/nuclei/templates/nuclei-parse-definition.yaml +++ b/scanners/nuclei/templates/nuclei-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/nuclei/values.yaml b/scanners/nuclei/values.yaml index d6fac86b14..16ca0973f5 100644 --- a/scanners/nuclei/values.yaml +++ b/scanners/nuclei/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/screenshooter/README.md b/scanners/screenshooter/README.md index ff5e4ed986..5479278c0b 100644 --- a/scanners/screenshooter/README.md +++ b/scanners/screenshooter/README.md @@ -66,6 +66,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-screenshooter"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/screenshooter/templates/screenshooter-parse-definition.yaml b/scanners/screenshooter/templates/screenshooter-parse-definition.yaml index bfb1253627..0d83f29343 100644 --- a/scanners/screenshooter/templates/screenshooter-parse-definition.yaml +++ b/scanners/screenshooter/templates/screenshooter-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/screenshooter/values.yaml b/scanners/screenshooter/values.yaml index 5f5362f996..229bb8ae9e 100644 --- a/scanners/screenshooter/values.yaml +++ b/scanners/screenshooter/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/semgrep/README.md b/scanners/semgrep/README.md index 55963e7038..9983ac5d20 100644 --- a/scanners/semgrep/README.md +++ b/scanners/semgrep/README.md @@ -178,6 +178,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | | | parser.image.repository | string | `"securecodebox/parser-semgrep"` | | | parser.image.tag | string | `nil` | | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | scanner.backoffLimit | int | `3` | | | scanner.env | list | `[]` | | | scanner.extraContainers | list | `[]` | | diff --git a/scanners/semgrep/templates/semgrep-parse-definition.yaml b/scanners/semgrep/templates/semgrep-parse-definition.yaml index e45c12a23f..c565e9861f 100644 --- a/scanners/semgrep/templates/semgrep-parse-definition.yaml +++ b/scanners/semgrep/templates/semgrep-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/semgrep/values.yaml b/scanners/semgrep/values.yaml index 8468a4311f..02bb74e731 100644 --- a/scanners/semgrep/values.yaml +++ b/scanners/semgrep/values.yaml @@ -7,6 +7,8 @@ parser: backoffLimit: 3 env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} scanner: image: diff --git a/scanners/ssh-scan/README.md b/scanners/ssh-scan/README.md index b12dd7af41..2503fcfa18 100644 --- a/scanners/ssh-scan/README.md +++ b/scanners/ssh-scan/README.md @@ -100,6 +100,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-ssh-scan"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/ssh-scan/templates/ssh-scan-parse-definition.yaml b/scanners/ssh-scan/templates/ssh-scan-parse-definition.yaml index 4b8c1b30f3..3c43579b05 100644 --- a/scanners/ssh-scan/templates/ssh-scan-parse-definition.yaml +++ b/scanners/ssh-scan/templates/ssh-scan-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/ssh-scan/values.yaml b/scanners/ssh-scan/values.yaml index 960d988e85..88568984ea 100644 --- a/scanners/ssh-scan/values.yaml +++ b/scanners/ssh-scan/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/sslyze/README.md b/scanners/sslyze/README.md index d3d26b415c..99cc3a960a 100644 --- a/scanners/sslyze/README.md +++ b/scanners/sslyze/README.md @@ -167,6 +167,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-sslyze"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/sslyze/templates/sslyze-parse-definition.yaml b/scanners/sslyze/templates/sslyze-parse-definition.yaml index 7c3f968a48..4151d995ae 100644 --- a/scanners/sslyze/templates/sslyze-parse-definition.yaml +++ b/scanners/sslyze/templates/sslyze-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/sslyze/values.yaml b/scanners/sslyze/values.yaml index 62f6c1dedf..df51d4c6e7 100644 --- a/scanners/sslyze/values.yaml +++ b/scanners/sslyze/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/test-scan/README.md b/scanners/test-scan/README.md index 6d289e3f84..77117741a9 100644 --- a/scanners/test-scan/README.md +++ b/scanners/test-scan/README.md @@ -60,6 +60,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-test-scan"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/test-scan/templates/test-scan-parse-definition.yaml b/scanners/test-scan/templates/test-scan-parse-definition.yaml index 06131c7190..4a802ea5d6 100644 --- a/scanners/test-scan/templates/test-scan-parse-definition.yaml +++ b/scanners/test-scan/templates/test-scan-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/test-scan/values.yaml b/scanners/test-scan/values.yaml index aa9163e960..a2840bc4bb 100644 --- a/scanners/test-scan/values.yaml +++ b/scanners/test-scan/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/trivy/README.md b/scanners/trivy/README.md index 9209399278..13db7f8a41 100644 --- a/scanners/trivy/README.md +++ b/scanners/trivy/README.md @@ -193,6 +193,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-trivy"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/trivy/templates/trivy-parse-definition.yaml b/scanners/trivy/templates/trivy-parse-definition.yaml index 4d502b3bd7..5c5a8fccaf 100644 --- a/scanners/trivy/templates/trivy-parse-definition.yaml +++ b/scanners/trivy/templates/trivy-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/trivy/values.yaml b/scanners/trivy/values.yaml index 378d8871d3..d46493794e 100644 --- a/scanners/trivy/values.yaml +++ b/scanners/trivy/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/typo3scan/templates/typo3scan-parse-definition.yaml b/scanners/typo3scan/templates/typo3scan-parse-definition.yaml index ca31754977..576814b774 100644 --- a/scanners/typo3scan/templates/typo3scan-parse-definition.yaml +++ b/scanners/typo3scan/templates/typo3scan-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/typo3scan/values.yaml b/scanners/typo3scan/values.yaml index a97ceef2fb..f826a2c27d 100644 --- a/scanners/typo3scan/values.yaml +++ b/scanners/typo3scan/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/whatweb/templates/whatweb-parse-definition.yaml b/scanners/whatweb/templates/whatweb-parse-definition.yaml index 1f2cf6bafd..84bbd21792 100644 --- a/scanners/whatweb/templates/whatweb-parse-definition.yaml +++ b/scanners/whatweb/templates/whatweb-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/whatweb/values.yaml b/scanners/whatweb/values.yaml index ea22d7b275..078ff0da95 100644 --- a/scanners/whatweb/values.yaml +++ b/scanners/whatweb/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/wpscan/README.md b/scanners/wpscan/README.md index 67b0bc2a33..2acba70480 100644 --- a/scanners/wpscan/README.md +++ b/scanners/wpscan/README.md @@ -106,6 +106,7 @@ Kubernetes: `>=v1.11.0-0` | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-wpscan"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/wpscan/templates/wpscan-parse-definition.yaml b/scanners/wpscan/templates/wpscan-parse-definition.yaml index b992674ec8..763fb786db 100644 --- a/scanners/wpscan/templates/wpscan-parse-definition.yaml +++ b/scanners/wpscan/templates/wpscan-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/wpscan/values.yaml b/scanners/wpscan/values.yaml index 2296184f69..1ea5f3e475 100644 --- a/scanners/wpscan/values.yaml +++ b/scanners/wpscan/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/zap-advanced/README.md b/scanners/zap-advanced/README.md index ab7210807f..5d0702ef92 100644 --- a/scanners/zap-advanced/README.md +++ b/scanners/zap-advanced/README.md @@ -484,6 +484,7 @@ zapConfiguration: | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-zap"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/zap-advanced/templates/zap-advanced-parse-definition.yaml b/scanners/zap-advanced/templates/zap-advanced-parse-definition.yaml index 7647e57835..0e5aba5295 100644 --- a/scanners/zap-advanced/templates/zap-advanced-parse-definition.yaml +++ b/scanners/zap-advanced/templates/zap-advanced-parse-definition.yaml @@ -14,3 +14,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/zap-advanced/values.yaml b/scanners/zap-advanced/values.yaml index a4327ccec0..b33cdef5ca 100644 --- a/scanners/zap-advanced/values.yaml +++ b/scanners/zap-advanced/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan diff --git a/scanners/zap/README.md b/scanners/zap/README.md index a537a52cd0..44f7ca7643 100644 --- a/scanners/zap/README.md +++ b/scanners/zap/README.md @@ -106,6 +106,7 @@ That's why we introduced this `zap-advanced` scanner chart, which introduces ext | parser.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images | | parser.image.repository | string | `"docker.io/securecodebox/parser-zap"` | Parser image repository | | parser.image.tag | string | defaults to the charts version | Parser image tag | +| parser.scopeLimiterAliases | object | `{}` | Optional finding aliases to be used in the scopeLimiter. | | parser.ttlSecondsAfterFinished | string | `nil` | seconds after which the kubernetes job for the parser will be deleted. Requires the Kubernetes TTLAfterFinished controller: https://kubernetes.io/docs/concepts/workloads/controllers/ttlafterfinished/ | | scanner.activeDeadlineSeconds | string | `nil` | There are situations where you want to fail a scan Job after some amount of time. To do so, set activeDeadlineSeconds to define an active deadline (in seconds) when considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#job-termination-and-cleanup) | | scanner.backoffLimit | int | 3 | There are situations where you want to fail a scan Job after some amount of retries due to a logical error in configuration etc. To do so, set backoffLimit to specify the number of retries before considering a scan Job as failed. (see: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy) | diff --git a/scanners/zap/templates/zap-parse-definition.yaml b/scanners/zap/templates/zap-parse-definition.yaml index 96737bc4e3..3d3d961d2a 100644 --- a/scanners/zap/templates/zap-parse-definition.yaml +++ b/scanners/zap/templates/zap-parse-definition.yaml @@ -12,3 +12,5 @@ spec: ttlSecondsAfterFinished: {{ .Values.parser.ttlSecondsAfterFinished }} env: {{- toYaml .Values.parser.env | nindent 4 }} + scopeLimiterAliases: + {{- toYaml .Values.parser.scopeLimiterAliases | nindent 4 }} diff --git a/scanners/zap/values.yaml b/scanners/zap/values.yaml index cb9b2887b0..b1aee17a7e 100644 --- a/scanners/zap/values.yaml +++ b/scanners/zap/values.yaml @@ -17,6 +17,9 @@ parser: # parser.env -- Optional environment variables mapped into each parseJob (see: https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) env: [] + # parser.scopeLimiterAliases -- Optional finding aliases to be used in the scopeLimiter. + scopeLimiterAliases: {} + scanner: image: # scanner.image.repository -- Container Image to run the scan