Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 30 additions & 26 deletions hooks/declarative-subsequent-scans/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,46 +57,50 @@ export function getCascadingScans(
findings: Array<Finding>,
cascadingRules: Array<CascadingRule>
): Array<ExtendedScanSpec> {
const cascadingScans: Array<ExtendedScanSpec> = [];
let cascadingScans: Array<ExtendedScanSpec> = [];
const cascadingRuleChain = getScanChain(parentScan);

const cascadingRuleChain = new Set<string>();
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<Finding>, cascadingRule: CascadingRule) {
const cascadingScans: Array<ExtendedScanSpec> = [];
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@
//
// 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");
});

test("should generate basic label string for multiple key values selector", () => {
expect(
generateLabelSelectorString({
generateSelectorString({
matchLabels: {
environment: "production",
team: "search"
Expand All @@ -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"
Expand All @@ -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",
Expand All @@ -56,7 +56,7 @@ test("should generate label string for set based expressions", () => {
).toBe("environment in (testing,development)");

expect(
generateLabelSelectorString({
generateSelectorString({
matchExpressions: [
{
key: "environment",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
51 changes: 26 additions & 25 deletions hooks/declarative-subsequent-scans/kubernetes-label-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,41 @@ export interface LabelSelector {
matchLabels: Map<string, string>;
}

// 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(", ");

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(",");
}
}
4 changes: 2 additions & 2 deletions hooks/declarative-subsequent-scans/scan-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as k8s from "@kubernetes/client-node";

import {
generateLabelSelectorString,
generateSelectorString,
LabelSelector
} from "./kubernetes-label-selector";

Expand Down Expand Up @@ -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}"`
Expand Down