hooks/cascading-scans/hook/hook.test.js
// SPDX-FileCopyrightText: the secureCodeBox authors
//
// SPDX-License-Identifier: Apache-2.0
const {getCascadingScans} = require("./hook");
const {
LabelSelectorRequirementOperator,
} = require("./kubernetes-label-selector");
const {ScopeLimiterRequirementOperator} = require("./scope-limiter");
let parentScan = undefined;
let sslyzeCascadingRules = undefined;
let parseDefinition = undefined;
beforeEach(() => {
parentScan = {
apiVersion: "execution.securecodebox.io/v1",
kind: "Scan",
metadata: {
name: "nmap-foobar.com",
annotations: {},
},
spec: {
scanType: "nmap",
parameters: "foobar.com",
cascades: {},
},
};
parseDefinition = {
meta: {},
spec: {
scopeLimiterAliases: {},
},
};
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
},
},
},
];
});
test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () => {
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).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [],
"volumes": [],
},
},
]
`);
});
test("Should create no subsequent scans if there are no rules", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadingRules = [];
const cascadedScans = getCascadingScans(
parentScan,
findings,
cascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`[]`);
});
test("Should not try to do magic to the scan name if its something random", () => {
parentScan.metadata.name = "foobar.com";
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: undefined,
ip_address: "10.42.42.42",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
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", () => {
parentScan.metadata.annotations["cascading.securecodebox.io/chain"] =
sslyzeCascadingRules[0].metadata.name;
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).toMatchInlineSnapshot(`[]`);
});
test("Should not crash when the annotations are not set", () => {
parentScan.metadata.annotations = undefined;
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).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [],
"volumes": [],
},
},
]
`);
});
test("Should copy ENV fields from cascadingRule to created scan", () => {
sslyzeCascadingRules[0].spec.scanSpec.env = [
{
name: "FOOBAR",
valueFrom: {secretKeyRef: {name: "foobar-token", key: "token"}},
},
];
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[0].spec.env).toMatchInlineSnapshot(`
[
{
"name": "FOOBAR",
"valueFrom": {
"secretKeyRef": {
"key": "token",
"name": "foobar-token",
},
},
},
]
`);
});
test("Should allow wildcards in cascadingRules", () => {
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 8443,
service: "https*",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
},
},
},
];
const findings = [
{
name: "Port 8443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 8443,
service: "https-alt",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [],
"parameters": [
"--regular",
"foobar.com:8443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [],
"volumes": [],
},
},
]
`);
});
test("should not copy labels if inheritLabels is set to false", () => {
parentScan.metadata.labels = {
organization: "OWASP",
location: "barcelona",
vlan: "lan",
};
parentScan.spec.cascades.inheritLabels = false;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
for (const cascadedScan of cascadedScans) {
expect(
Object.entries(parentScan.metadata.labels).every(
([label, value]) => cascadedScan.metadata.labels[label] === value
)
).toBe(false);
}
});
test("should copy labels if inheritLabels is not set", () => {
parentScan.metadata.labels = {
organization: "OWASP",
location: "barcelona",
vlan: "lan",
};
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
for (const cascadedScan of cascadedScans) {
expect(
Object.entries(parentScan.metadata.labels).every(
([label, value]) => cascadedScan.metadata.labels[label] === value
)
).toBe(true);
}
});
test("should copy labels if inheritLabels is set to true", () => {
parentScan.metadata.labels = {
organization: "OWASP",
location: "barcelona",
vlan: "lan",
};
parentScan.spec.cascades.inheritLabels = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
for (const cascadedScan of cascadedScans) {
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",
};
parentScan.spec.cascades.inheritAnnotations = false;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
for (const cascadedScan of cascadedScans) {
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",
};
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
);
for (const cascadedScan of cascadedScans) {
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",
};
parentScan.spec.cascades.inheritAnnotations = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
for (const cascadedScan of cascadedScans) {
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",
};
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
);
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",
};
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
);
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}}",
};
sslyzeCascadingRules[0].spec.scanLabels = {
k_one: "{{metadata.name}}",
k_two: "{{metadata.unknown_property}}",
k_three: "{{$.hostOrIP}}",
};
const findings = [
{
name: "Port 8443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 8443,
service: "https",
},
},
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
sslyzeCascadingRules[0],
parseDefinition
);
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",
};
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",
};
expect(annotations).toEqual(annotationsResults);
});
test("should copy proper finding ID into annotations", () => {
const findings = [
{
name: "Port 12345 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 12345,
service: "unknown",
},
id: "random-id",
},
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
id: "f0c718bd-9987-42c8-2259-73794e61dd5a",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
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);
});
test("should merge environment variables into cascaded scan", () => {
parentScan.spec.cascades.inheritEnv = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.env = [
{
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",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.env).toMatchInlineSnapshot(`
[
{
"name": "parent_environment_variable_name",
"value": "parent_environment_variable_value",
},
{
"name": "rule_environment_variable_name",
"value": "rule_environment_variable_value",
},
]
`);
});
test("should merge volumeMounts into cascaded scan", () => {
parentScan.spec.cascades.inheritVolumes = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.volumeMounts = [
{
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",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.volumeMounts).toMatchInlineSnapshot(`
[
{
"mountPath": "/etc/ssl/certs/ca-cert.cer",
"name": "ca-certificate",
"readOnly": true,
"subPath": "ca-cert.cer",
},
{
"mountPath": "/etc/ssl/certs/ca-cert-sslyze.cer",
"name": "ca-certificate-sslyze",
"readOnly": true,
"subPath": "ca-cert-sslyze.cer",
},
]
`);
});
test("should merge volumes into cascaded scan", () => {
parentScan.spec.cascades.inheritVolumes = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.volumes = [
{
name: "ca-certificate",
configMap: {
name: "ca-certificate",
},
},
];
sslyzeCascadingRules[0].spec.scanSpec.volumes = [
{
name: "ca-certificate-sslyze",
configMap: {
name: "ca-certificate-sslyze",
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.volumes).toMatchInlineSnapshot(`
[
{
"configMap": {
"name": "ca-certificate",
},
"name": "ca-certificate",
},
{
"configMap": {
"name": "ca-certificate-sslyze",
},
"name": "ca-certificate-sslyze",
},
]
`);
});
test("should merge initContainers into cascaded scan", () => {
parentScan.spec.cascades.inheritInitContainers = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.initContainers = [
{
name: "test-init",
image: "bitnami/git",
command: ["whoami"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.initContainers = [
{
name: "test-init-2",
image: "some/hypothetical",
command: ["echo", "1"],
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.initContainers).toMatchInlineSnapshot(`
[
{
"command": [
"whoami",
],
"image": "bitnami/git",
"name": "test-init",
},
{
"command": [
"echo",
"1",
],
"image": "some/hypothetical",
"name": "test-init-2",
},
]
`);
});
test("should not merge initContainers into cascaded scan if not instructed", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.initContainers = [
{
name: "test-init",
image: "bitnami/git",
command: ["whoami"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.initContainers = [
{
name: "test-init-2",
image: "some/hypothetical",
command: ["echo", "1"],
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.initContainers).toMatchInlineSnapshot(`
[
{
"command": [
"echo",
"1",
],
"image": "some/hypothetical",
"name": "test-init-2",
},
]
`);
});
test("Templating should apply to environment variables", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
volumes: [{name: "test-volume", emptyDir: {}}],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
env: [{name: "HostOrIp", value: "{{$.hostOrIP}}"}],
},
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [
{
"name": "HostOrIp",
"value": "foobar.com",
},
],
"hookSelector": {},
"initContainers": [],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
"volumes": [
{
"emptyDir": {},
"name": "test-volume",
},
],
},
},
]
`);
});
test("Templating should apply to initContainer commands", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
volumes: [{name: "test-volume", emptyDir: {}}],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
initContainers: [
{
name: "ping-it-again",
image: "busybox",
command: ["ping", "-c", "1", "{{$.hostOrIP}}"],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
},
],
},
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [
{
"command": [
"ping",
"-c",
"1",
"foobar.com",
],
"image": "busybox",
"name": "ping-it-again",
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
},
],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
"volumes": [
{
"emptyDir": {},
"name": "test-volume",
},
],
},
},
]
`);
});
test("Templating should apply to initContainer environment variables", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
volumes: [{name: "test-volume", emptyDir: {}}],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
initContainers: [
{
name: "ping-it-again",
image: "busybox",
command: ["whoami"],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
env: [{name: "HostOrIp", value: "{{$.hostOrIP}}"}],
},
],
},
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [
{
"command": [
"whoami",
],
"env": [
{
"name": "HostOrIp",
"value": "foobar.com",
},
],
"image": "busybox",
"name": "ping-it-again",
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
},
],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
"volumes": [
{
"emptyDir": {},
"name": "test-volume",
},
],
},
},
]
`);
});
test("Templating should not break special encoding (http://...) when using triple-mustache {{{}}}", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "https://github.com/secureCodeBox/secureCodeBox",
port: 443,
service: "https",
},
},
];
sslyzeCascadingRules = [
{
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{{attributes.hostname}}}"],
volumes: [{name: "test-volume", emptyDir: {}}],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
initContainers: [
{
name: "ping-it-again",
image: "busybox",
command: ["ping", "-c", "1", "{{{attributes.hostname}}}"],
volumeMounts: [{name: "test-volume", mountPath: "/test"}],
},
],
},
},
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
expect(cascadedScans).toMatchInlineSnapshot(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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",
},
"generateName": "sslyze-foobar.com-tls-scans-",
"labels": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {},
"env": [],
"hookSelector": {},
"initContainers": [
{
"command": [
"ping",
"-c",
"1",
"https://github.com/secureCodeBox/secureCodeBox",
],
"image": "busybox",
"name": "ping-it-again",
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
},
],
"parameters": [
"--regular",
"https://github.com/secureCodeBox/secureCodeBox",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [
{
"mountPath": "/test",
"name": "test-volume",
},
],
"volumes": [
{
"emptyDir": {},
"name": "test-volume",
},
],
},
},
]
`);
});
test("should merge hookSelector into cascaded scan if inheritHookSelector is enabled", () => {
parentScan.spec.cascades.inheritHookSelector = true;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
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"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {};
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [
{
key: "securecodebox.io/name",
operator: LabelSelectorRequirementOperator.NotIn,
values: ["cascading-scans"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = {
"securecodebox.io/internal": "false",
};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.hookSelector).toMatchInlineSnapshot(`
{
"matchExpressions": [
{
"key": "securecodebox.io/name",
"operator": "In",
"values": [
"cascading-scans",
],
},
{
"key": "securecodebox.io/name",
"operator": "NotIn",
"values": [
"cascading-scans",
],
},
],
"matchLabels": {
"securecodebox.io/internal": "false",
},
}
`);
});
test("should not merge hookSelector into cascaded scan if inheritHookSelector is disabled", () => {
parentScan.spec.cascades.inheritHookSelector = false;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
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"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {};
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [
{
key: "securecodebox.io/name",
operator: LabelSelectorRequirementOperator.NotIn,
values: ["cascading-scans"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = {
"securecodebox.io/internal": "false",
};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.hookSelector).toMatchInlineSnapshot(`
{
"matchExpressions": [
{
"key": "securecodebox.io/name",
"operator": "NotIn",
"values": [
"cascading-scans",
],
},
],
"matchLabels": {
"securecodebox.io/internal": "false",
},
}
`);
});
test("should copy tolerations and affinity into cascaded scan if one is set and label is unset", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "disktype",
operator: "In",
values: ["ssd"],
},
],
},
],
},
},
};
parentScan.spec.tolerations = [
{
key: "key1",
operator: "Equal",
value: "test",
effect: "NoSchedule",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`
{
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "disktype",
"operator": "In",
"values": [
"ssd",
],
},
],
},
],
},
},
}
`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`
[
{
"effect": "NoSchedule",
"key": "key1",
"operator": "Equal",
"value": "test",
},
]
`);
});
test("should not copy tolerations and affinity into cascaded scan if label disables it", () => {
parentScan.spec.cascades.inheritAffinity = false;
parentScan.spec.cascades.inheritTolerations = false;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "disktype",
operator: "In",
values: ["ssd"],
},
],
},
],
},
},
};
parentScan.spec.tolerations = [
{
key: "key1",
operator: "Equal",
value: "test",
effect: "NoSchedule",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`undefined`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`undefined`);
});
test("should merge tolerations and replace affinity in cascaded scan if cascading spec sets new ones", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "disktype",
operator: "In",
values: ["ssd"],
},
],
},
],
},
},
};
parentScan.spec.tolerations = [
{
key: "key1",
operator: "Equal",
value: "test",
effect: "NoSchedule",
},
];
sslyzeCascadingRules[0].spec.scanSpec.tolerations = [
{
key: "key2",
operator: "Equal",
value: "test-2",
effect: "NoSchedule",
},
];
sslyzeCascadingRules[0].spec.scanSpec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "network",
operator: "In",
values: ["10g"],
},
],
},
],
},
},
};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
// New values will completely replace the old values, not be merged
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`
{
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "network",
"operator": "In",
"values": [
"10g",
],
},
],
},
],
},
},
}
`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`
[
{
"effect": "NoSchedule",
"key": "key1",
"operator": "Equal",
"value": "test",
},
{
"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", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.affinity = {};
parentScan.spec.tolerations = [];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
// New values will completely replace the old values, not be merged
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`{}`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`[]`);
});
test("Should not set affinity or tolerations to undefined if they are defined to be an empty map / list in cascading ScanSpec", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
sslyzeCascadingRules[0].spec.scanSpec.tolerations = [];
sslyzeCascadingRules[0].spec.scanSpec.affinity = {};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
// New values will completely replace the old values, not be merged
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`{}`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`[]`);
});
test("should only use tolerations and affinity of cascaded scan if inheritance is disabled", () => {
parentScan.spec.cascades.inheritAffinity = false;
parentScan.spec.cascades.inheritTolerations = false;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "disktype",
operator: "In",
values: ["ssd"],
},
],
},
],
},
},
};
parentScan.spec.tolerations = [
{
key: "key1",
operator: "Equal",
value: "test",
effect: "NoSchedule",
},
];
sslyzeCascadingRules[0].spec.scanSpec.tolerations = [
{
key: "key2",
operator: "Equal",
value: "test-2",
effect: "NoSchedule",
},
];
sslyzeCascadingRules[0].spec.scanSpec.affinity = {
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: [
{
key: "network",
operator: "In",
values: ["10g"],
},
],
},
],
},
},
};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
expect(cascadedScan.spec.affinity).toMatchInlineSnapshot(`
{
"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [
{
"matchExpressions": [
{
"key": "network",
"operator": "In",
"values": [
"10g",
],
},
],
},
],
},
},
}
`);
expect(cascadedScan.spec.tolerations).toMatchInlineSnapshot(`
[
{
"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;
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.volumes = [
{
name: "ca-certificate",
configMap: {
name: "ca-certificate",
},
},
];
sslyzeCascadingRules[0].spec.scanSpec.volumes = [
{
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",
},
];
sslyzeCascadingRules[0].spec.scanSpec.volumeMounts = [
{
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",
},
];
sslyzeCascadingRules[0].spec.scanSpec.env = [
{
name: "rule_environment_variable_name",
value: "rule_environment_variable_value",
},
];
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"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector = {};
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchExpressions = [
{
key: "securecodebox.io/name",
operator: LabelSelectorRequirementOperator.NotIn,
values: ["cascading-scans"],
},
];
sslyzeCascadingRules[0].spec.scanSpec.hookSelector.matchLabels = {
"securecodebox.io/internal": "false",
};
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
// Create a second cascading rule
sslyzeCascadingRules[1] = {
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans-second",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
},
},
};
cascadedScan.metadata.name = cascadedScan.metadata.generateName;
const secondCascadedScans = getCascadingScans(
cascadedScan,
findings,
sslyzeCascadingRules,
sslyzeCascadingRules[0], // cascaded rule on parent
parseDefinition
);
const secondCascadedScan = secondCascadedScans[0];
expect(secondCascadedScan.spec.env).toMatchInlineSnapshot(`
[
{
"name": "parent_environment_variable_name",
"value": "parent_environment_variable_value",
},
]
`);
expect(secondCascadedScan.spec.volumes).toMatchInlineSnapshot(`
[
{
"configMap": {
"name": "ca-certificate",
},
"name": "ca-certificate",
},
]
`);
expect(secondCascadedScan.spec.volumeMounts).toMatchInlineSnapshot(`
[
{
"mountPath": "/etc/ssl/certs/ca-cert.cer",
"name": "ca-certificate",
"readOnly": true,
"subPath": "ca-cert.cer",
},
]
`);
expect(secondCascadedScan.spec.hookSelector.matchExpressions)
.toMatchInlineSnapshot(`
[
{
"key": "securecodebox.io/name",
"operator": "In",
"values": [
"cascading-scans",
],
},
]
`);
expect(
secondCascadedScan.spec.hookSelector.matchLabels
).toMatchInlineSnapshot(`{}`);
});
test("should not copy cascaded scan spec from parent scan if inheritance is undefined", () => {
const findings = [
{
name: "Port 443 is open",
category: "Open Port",
attributes: {
state: "open",
hostname: "foobar.com",
port: 443,
service: "https",
},
},
];
parentScan.spec.volumes = [
{
name: "ca-certificate",
configMap: {
name: "ca-certificate",
},
},
];
sslyzeCascadingRules[0].spec.scanSpec.volumes = [
{
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",
},
];
sslyzeCascadingRules[0].spec.scanSpec.volumeMounts = [
{
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",
},
];
sslyzeCascadingRules[0].spec.scanSpec.env = [
{
name: "rule_environment_variable_name",
value: "rule_environment_variable_value",
},
];
const cascadedScans = getCascadingScans(
parentScan,
findings,
sslyzeCascadingRules,
undefined,
parseDefinition
);
const cascadedScan = cascadedScans[0];
// Create a second cascading rule
sslyzeCascadingRules[1] = {
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans-second",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
},
},
};
cascadedScan.metadata.name = cascadedScan.metadata.generateName;
const secondCascadedScans = getCascadingScans(
cascadedScan,
findings,
sslyzeCascadingRules,
sslyzeCascadingRules[0], // cascaded rule on parent
parseDefinition
);
const secondCascadedScan = secondCascadedScans[0];
expect(secondCascadedScan.spec.env).toMatchInlineSnapshot(`[]`);
expect(secondCascadedScan.spec.volumes).toMatchInlineSnapshot(`[]`);
expect(secondCascadedScan.spec.volumeMounts).toMatchInlineSnapshot(`[]`);
});
test("should append cascading rule to further cascading scan chains", () => {
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
);
const cascadedScan = cascadedScans[0];
// Create a second cascading rule
sslyzeCascadingRules[1] = {
apiVersion: "cascading.securecodebox.io/v1",
kind: "CascadingRule",
metadata: {
name: "tls-scans-second",
},
spec: {
matches: {
anyOf: [
{
category: "Open Port",
attributes: {
port: 443,
service: "https",
},
},
{
category: "Open Port",
attributes: {
service: "https",
},
},
],
},
scanSpec: {
scanType: "sslyze",
parameters: ["--regular", "{{$.hostOrIP}}:{{attributes.port}}"],
},
},
};
cascadedScan.metadata.name = cascadedScan.metadata.generateName;
const secondCascadedScans = getCascadingScans(
cascadedScan,
findings,
sslyzeCascadingRules,
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");
});
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(`
[
{
"apiVersion": "execution.securecodebox.io/v1",
"kind": "Scan",
"metadata": {
"annotations": {
"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": {},
"ownerReferences": [
{
"apiVersion": "execution.securecodebox.io/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Scan",
"name": "nmap-foobar.com",
"uid": undefined,
},
],
},
"spec": {
"affinity": undefined,
"cascades": {
"scopeLimiter": {
"allOf": [
{
"key": "scope.cascading.securecodebox.io/ports",
"operator": "Contains",
"values": [
"{{$.port}}",
],
},
],
},
},
"env": [],
"hookSelector": {},
"initContainers": [],
"parameters": [
"--regular",
"foobar.com:443",
],
"resourceMode": "namespaceLocal",
"scanType": "sslyze",
"tolerations": undefined,
"volumeMounts": [],
"volumes": [],
},
},
]
`);
});
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(`
{
"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",
}
`);
});