diff --git a/hooks/cascading-scans/hook.test.js b/hooks/cascading-scans/hook.test.js index 8321521a1b..4477164309 100644 --- a/hooks/cascading-scans/hook.test.js +++ b/hooks/cascading-scans/hook.test.js @@ -80,8 +80,18 @@ test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () = expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -143,8 +153,19 @@ test("Should not try to do magic to the scan name if its something random", () = expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": undefined, + "ip_address": "10.42.42.42", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "foobar.com-tls-scans", "parameters": Array [ @@ -210,8 +231,18 @@ test("Should not crash when the annotations are not set", () => { expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -256,7 +287,7 @@ test("Should copy ENV fields from cascadingRule to created scan", () => { expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": Array [ Object { "name": "FOOBAR", @@ -268,6 +299,16 @@ test("Should copy ENV fields from cascadingRule to created scan", () => { }, }, ], + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -332,8 +373,18 @@ test("Should allow wildcards in cascadingRules", () => { expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 8443, + "service": "https-alt", + "state": "open", + }, + "category": "Open Port", + "name": "Port 8443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -375,17 +426,8 @@ test("should not copy labels if inheritLabels is set to false", () => { sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => cascadingScanDefinition.metadata.labels[label] === value @@ -419,17 +461,8 @@ test("should copy labels if inheritLabels is not set", () => { sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => cascadingScanDefinition.metadata.labels[label] === value @@ -465,17 +498,8 @@ test("should copy labels if inheritLabels is set to true", () => { sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.labels).every(([label, value]) => cascadingScanDefinition.metadata.labels[label] === value @@ -509,17 +533,8 @@ test("should not copy annotations if inheritAnnotations is set to false", () => sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => cascadingScanDefinition.metadata.annotations[label] === value @@ -552,17 +567,8 @@ test("should copy annotations if inheritAnnotations is not set", () => { sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => cascadingScanDefinition.metadata.annotations[label] === value @@ -596,17 +602,8 @@ test("should copy annotations if inheritAnnotations is set to true", () => { sslyzeCascadingRules ); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadedScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadedScan of cascadedScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(parentScan.metadata.annotations).every(([label, value]) => cascadingScanDefinition.metadata.annotations[label] === value @@ -639,13 +636,23 @@ test("should copy scanLabels from CascadingRule to cascading scan", () => { sslyzeCascadingRules ); - const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } = cascadedScans[0] + const cascadedScan = cascadedScans[0] expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -662,16 +669,7 @@ test("should copy scanLabels from CascadingRule to cascading scan", () => { ] `); - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(sslyzeCascadingRules[0].spec.scanLabels).every(([label, value]) => cascadingScanDefinition.metadata.labels[label] === value @@ -703,13 +701,23 @@ test("should copy scanAnnotations from CascadingRule to cascading scan", () => { sslyzeCascadingRules ); - const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } = cascadedScans[0] + const cascadedScan = cascadedScans[0] expect(cascadedScans).toMatchInlineSnapshot(` Array [ Object { - "cascades": null, + "cascades": Object {}, "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "name": "Port 443 is open", + }, "generatedBy": "tls-scans", "name": "sslyze-foobar.com-tls-scans", "parameters": Array [ @@ -726,16 +734,7 @@ test("should copy scanAnnotations from CascadingRule to cascading scan", () => { ] `); - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: parentScan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); expect(Object.entries(sslyzeCascadingRules[0].spec.scanAnnotations).every(([label, value]) => cascadingScanDefinition.metadata.annotations[label] === value @@ -788,3 +787,75 @@ test("should properly parse template values in scanLabels and scanAnnotations", expect(scanAnnotations).toEqual(result) }) +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 + ); + + const cascadedScan = cascadedScans[0] + + expect(cascadedScans).toMatchInlineSnapshot(` + Array [ + Object { + "cascades": Object {}, + "env": undefined, + "finding": Object { + "attributes": Object { + "hostname": "foobar.com", + "port": 443, + "service": "https", + "state": "open", + }, + "category": "Open Port", + "id": "f0c718bd-9987-42c8-2259-73794e61dd5a", + "name": "Port 443 is open", + }, + "generatedBy": "tls-scans", + "name": "sslyze-foobar.com-tls-scans", + "parameters": Array [ + "--regular", + "foobar.com:443", + ], + "scanAnnotations": Object {}, + "scanLabels": Object {}, + "scanType": "sslyze", + }, + ] + `); + + const cascadingScanDefinition = getCascadingScanDefinition(cascadedScan, parentScan); + + expect(Object.entries(cascadingScanDefinition.metadata.annotations).every(([label, value]) => { + if (label === "cascading.securecodebox.io/matched-finding") { + return value === "f0c718bd-9987-42c8-2259-73794e61dd5a"; + } else return true; + } + )).toBe(true) +}); diff --git a/hooks/cascading-scans/hook.ts b/hooks/cascading-scans/hook.ts index 573e073aaa..260826b680 100644 --- a/hooks/cascading-scans/hook.ts +++ b/hooks/cascading-scans/hook.ts @@ -28,17 +28,8 @@ export async function handle({ scan, getFindings }: HandleArgs) { const cascadingScans = getCascadingScans(scan, findings, cascadingRules); - for (const { name, scanType, parameters, generatedBy, env, scanLabels, scanAnnotations } of cascadingScans) { - const cascadingScanDefinition = getCascadingScanDefinition({ - name, - parentScan: scan, - generatedBy, - scanType, - parameters, - env, - scanLabels, - scanAnnotations - }); + for (const cascadingScan of cascadingScans) { + const cascadingScanDefinition = getCascadingScanDefinition(cascadingScan, scan); await startSubsequentSecureCodeBoxScan(cascadingScanDefinition); } } @@ -127,13 +118,14 @@ function getCascadingScan( parameters: parameters.map(parameter => Mustache.render(parameter, templateArgs) ), - cascades: null, + cascades: parentScan.spec.cascades, generatedBy: cascadingRule.metadata.name, env, scanLabels: cascadingRule.spec.scanLabels === undefined ? {} : mapValues(cascadingRule.spec.scanLabels, value => Mustache.render(value, templateArgs)), scanAnnotations: cascadingRule.spec.scanAnnotations === undefined ? {} : mapValues(cascadingRule.spec.scanAnnotations, value => Mustache.render(value, templateArgs)), + finding }; } diff --git a/hooks/cascading-scans/scan-helpers.ts b/hooks/cascading-scans/scan-helpers.ts index 9ba0f19b14..34f3b280af 100644 --- a/hooks/cascading-scans/scan-helpers.ts +++ b/hooks/cascading-scans/scan-helpers.ts @@ -24,6 +24,7 @@ export interface Finding { severity: string; osi_layer: string; attributes: Map; + id: string; } export interface CascadingRule { @@ -80,18 +81,22 @@ export interface ExtendedScanSpec extends ScanSpec { scanAnnotations: { [key: string]: string; }; + + // Finding that triggered the scan + finding: Finding } export function getCascadingScanDefinition({ name, - parentScan, scanType, parameters, generatedBy, env, + cascades, scanLabels, - scanAnnotations - }) { + scanAnnotations, + finding + }: ExtendedScanSpec, parentScan: Scan) { function mergeInherited(parentProps, ruleProps, inherit: boolean = true) { if (!inherit) { parentProps = {}; @@ -126,6 +131,7 @@ export function getCascadingScanDefinition({ annotations: { "securecodebox.io/hook": "cascading-scans", "cascading.securecodebox.io/parent-scan": parentScan.metadata.name, + "cascading.securecodebox.io/matched-finding": finding.id, "cascading.securecodebox.io/chain": [ ...cascadingChain, generatedBy @@ -146,7 +152,7 @@ export function getCascadingScanDefinition({ spec: { scanType, parameters, - cascades: parentScan.spec.cascades, + cascades, env, } };