diff --git a/hooks/declarative-subsequent-scans/hook.ts b/hooks/declarative-subsequent-scans/hook.ts index 789dd47899..33bd4659f7 100644 --- a/hooks/declarative-subsequent-scans/hook.ts +++ b/hooks/declarative-subsequent-scans/hook.ts @@ -57,46 +57,50 @@ export function getCascadingScans( findings: Array, cascadingRules: Array ): Array { - const cascadingScans: Array = []; + let cascadingScans: Array = []; + const cascadingRuleChain = getScanChain(parentScan); - const cascadingRuleChain = new Set(); + for (const cascadingRule of cascadingRules) { + // Check if the Same CascadingRule was already applied in the Cascading Chain + // If it has already been used skip this rule as it could potentially lead to loops + if (cascadingRuleChain.includes(cascadingRule.metadata.name)) { + console.log( + `Skipping Rule "${cascadingRule.metadata.name}" as it was already applied in this chain.` + ); + continue; + } + + cascadingScans = cascadingScans.concat(getScansMatchingRule(parentScan, findings, cascadingRule)) + } + return cascadingScans; +} + +function getScanChain(parentScan: Scan) { // Get the current Scan Chain (meaning which CascadingRules were used to start this scan and its parents) and convert it to a set, which makes it easier to query. if ( parentScan.metadata.annotations && parentScan.metadata.annotations["cascading.securecodebox.io/chain"] ) { - const chainElements = parentScan.metadata.annotations[ + return parentScan.metadata.annotations[ "cascading.securecodebox.io/chain" ].split(","); - - for (const element of chainElements) { - cascadingRuleChain.add(element); - } } + return [] +} - for (const cascadingRule of cascadingRules) { - // Check if the Same CascadingRule was already applied in the Cascading Chain - // If it has already been used skip this rule as it could potentially lead to loops - if (cascadingRuleChain.has(cascadingRule.metadata.name)) { - console.log( - `Skipping Rule "${cascadingRule.metadata.name}" as it was already applied in this chain.` - ); - continue; - } - - for (const finding of findings) { - // 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) - ); +function getScansMatchingRule(parentScan: Scan, findings: Array, cascadingRule: CascadingRule) { + const cascadingScans: Array = []; + for (const finding of findings) { + // 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) + ); - if (matches) { - cascadingScans.push(getCascadingScan(parentScan, finding, cascadingRule)) - } + if (matches) { + cascadingScans.push(getCascadingScan(parentScan, finding, cascadingRule)) } } - return cascadingScans; } diff --git a/hooks/declarative-subsequent-scans/kubernetes-label-selector.test.js b/hooks/declarative-subsequent-scans/kubernetes-label-selector.test.js index 7ad6ffbade..d7bdd6eb95 100644 --- a/hooks/declarative-subsequent-scans/kubernetes-label-selector.test.js +++ b/hooks/declarative-subsequent-scans/kubernetes-label-selector.test.js @@ -2,21 +2,21 @@ // // SPDX-License-Identifier: Apache-2.0 -const { generateLabelSelectorString } = require("./kubernetes-label-selector"); +const { generateSelectorString } = require("./kubernetes-label-selector"); test("should generate a empty string if passed an empty object", () => { - expect(generateLabelSelectorString({})).toBe(""); + expect(generateSelectorString({})).toBe(""); }); test("should generate basic label string for key values selector", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchLabels: { environment: "production" } }) ).toBe("environment=production"); expect( - generateLabelSelectorString({ + generateSelectorString({ matchLabels: { environment: "testing" } }) ).toBe("environment=testing"); @@ -24,7 +24,7 @@ test("should generate basic label string for key values selector", () => { test("should generate basic label string for multiple key values selector", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchLabels: { environment: "production", team: "search" @@ -33,7 +33,7 @@ test("should generate basic label string for multiple key values selector", () = ).toBe("environment=production,team=search"); expect( - generateLabelSelectorString({ + generateSelectorString({ matchLabels: { environment: "testing", team: "payment" @@ -44,7 +44,7 @@ test("should generate basic label string for multiple key values selector", () = test("should generate label string for set based expressions", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", @@ -56,7 +56,7 @@ test("should generate label string for set based expressions", () => { ).toBe("environment in (testing,development)"); expect( - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", @@ -70,7 +70,7 @@ test("should generate label string for set based expressions", () => { test("should generate label string for set based expressions with multiple entries", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", @@ -89,7 +89,7 @@ test("should generate label string for set based expressions with multiple entri test("should generate label string for set based Exists and DoesNotExist operators", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", @@ -106,7 +106,7 @@ test("should generate label string for set based Exists and DoesNotExist operato test("should generate selectors with both expression and labelMatching", () => { expect( - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", @@ -138,7 +138,7 @@ test("should generate selectors with both expression and labelMatching", () => { test("should throw a exception when passed a unknown operator", () => { expect(() => - generateLabelSelectorString({ + generateSelectorString({ matchExpressions: [ { key: "environment", diff --git a/hooks/declarative-subsequent-scans/kubernetes-label-selector.ts b/hooks/declarative-subsequent-scans/kubernetes-label-selector.ts index de0caff44e..e38fa18906 100644 --- a/hooks/declarative-subsequent-scans/kubernetes-label-selector.ts +++ b/hooks/declarative-subsequent-scans/kubernetes-label-selector.ts @@ -23,31 +23,35 @@ export interface LabelSelector { matchLabels: Map; } -// generateLabelSelectorString transforms a kubernetes labelSelector object in to the string representation -export function generateLabelSelectorString({ +// generateSelectorString transforms a kubernetes labelSelector object in to the string representation +export function generateSelectorString({ matchExpressions = [], matchLabels = new Map() }: LabelSelector): string { - const matchLabelsSelector = Array.from(Object.entries(matchLabels)).map( - ([key, values]) => `${key}=${values}` - ); - - const matchExpressionsSelector = matchExpressions.map( - ({ key, values, operator }) => { - if ( - operator === LabelSelectorRequirementOperator.In || - operator === LabelSelectorRequirementOperator.NotIn - ) { - return `${key} ${operator.toLowerCase()} (${values.join(",")})`; - } - - if (operator === LabelSelectorRequirementOperator.Exists) { - return key; - } - if (operator === LabelSelectorRequirementOperator.DoesNotExist) { - return `!${key}`; - } + const matchLabelsSelector = Array.from(Object.entries(matchLabels)).map(generateLabelsSelectorString); + const matchExpressionsSelector = matchExpressions.map(generateExpressionsSelectorString); + + return [...matchLabelsSelector, ...matchExpressionsSelector].join(","); +} + +function generateLabelsSelectorString([key, values]) { + return `${key}=${values}` +} + +function generateExpressionsSelectorString({key, values, operator}: LabelSelectorRequirement) { + switch (operator) { + case LabelSelectorRequirementOperator.In: + case LabelSelectorRequirementOperator.NotIn: + return `${key} ${operator.toLowerCase()} (${values.join(",")})`; + + case LabelSelectorRequirementOperator.Exists: + return key; + + case LabelSelectorRequirementOperator.DoesNotExist: + return `!${key}`; + + default: const supportedOperators = Object.values( LabelSelectorRequirementOperator ).join(", "); @@ -55,8 +59,5 @@ export function generateLabelSelectorString({ throw new Error( `Unknown LabelSelector Operator "${operator}". Supported are (${supportedOperators}). If this is an official label selector operator in kubernetes please open up a issue in the secureCodeBox Repo.` ); - } - ); - - return [...matchLabelsSelector, ...matchExpressionsSelector].join(","); + } } diff --git a/hooks/declarative-subsequent-scans/scan-helpers.ts b/hooks/declarative-subsequent-scans/scan-helpers.ts index a41cc476e6..23bbb69a83 100644 --- a/hooks/declarative-subsequent-scans/scan-helpers.ts +++ b/hooks/declarative-subsequent-scans/scan-helpers.ts @@ -5,7 +5,7 @@ import * as k8s from "@kubernetes/client-node"; import { - generateLabelSelectorString, + generateSelectorString, LabelSelector } from "./kubernetes-label-selector"; @@ -178,7 +178,7 @@ export async function getCascadingRulesForScan(scan: Scan) { } try { - const labelSelector = generateLabelSelectorString(scan.spec.cascades); + const labelSelector = generateSelectorString(scan.spec.cascades); console.log( `Fetching CascadingScans using LabelSelector: "${labelSelector}"`