From 8185951bbf8942caf917ec8e446949388de2f5c1 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 4 Oct 2021 12:00:56 +0200 Subject: [PATCH 01/11] Adding empty result files and licenses Added empty test files where they were missing Signed-off-by: Sebastian --- .../kube-hunter/parser/__testFiles__/test-empty-report.json | 1 + .../parser/__testFiles__/test-empty-report.json.license | 3 +++ .../kubeaudit/parser/__testFiles__/test-empty-report.jsonl | 1 + .../parser/__testFiles__/test-empty-report.jsonl.license | 3 +++ scanners/nikto/parser/__testFiles__/test-empty-report.json | 1 + .../nikto/parser/__testFiles__/test-empty-report.json.license | 3 +++ scanners/ssh-scan/parser/__testFiles__/test-empty-report.json | 1 + .../parser/__testFiles__/test-empty-report.json.license | 3 +++ scanners/sslyze/parser/__testFiles__/test-empty-report.json | 1 + .../sslyze/parser/__testFiles__/test-empty-report.json.license | 3 +++ scanners/trivy/parser/__testFiles__/test-empty-report.json | 1 + .../trivy/parser/__testFiles__/test-empty-report.json.license | 3 +++ scanners/typo3scan/parser/__testFiles__/test-empty-report.json | 1 + .../parser/__testFiles__/test-empty-report.json.license | 3 +++ 14 files changed, 28 insertions(+) create mode 100644 scanners/kube-hunter/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/kube-hunter/parser/__testFiles__/test-empty-report.json.license create mode 100644 scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl create mode 100644 scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl.license create mode 100644 scanners/nikto/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/nikto/parser/__testFiles__/test-empty-report.json.license create mode 100644 scanners/ssh-scan/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/ssh-scan/parser/__testFiles__/test-empty-report.json.license create mode 100644 scanners/sslyze/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/sslyze/parser/__testFiles__/test-empty-report.json.license create mode 100644 scanners/trivy/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/trivy/parser/__testFiles__/test-empty-report.json.license create mode 100644 scanners/typo3scan/parser/__testFiles__/test-empty-report.json create mode 100644 scanners/typo3scan/parser/__testFiles__/test-empty-report.json.license diff --git a/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json b/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json.license b/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/kube-hunter/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl b/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl @@ -0,0 +1 @@ +[] diff --git a/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl.license b/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/kubeaudit/parser/__testFiles__/test-empty-report.jsonl.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/nikto/parser/__testFiles__/test-empty-report.json b/scanners/nikto/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/nikto/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/nikto/parser/__testFiles__/test-empty-report.json.license b/scanners/nikto/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/nikto/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json b/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json.license b/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/ssh-scan/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/sslyze/parser/__testFiles__/test-empty-report.json b/scanners/sslyze/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/sslyze/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/sslyze/parser/__testFiles__/test-empty-report.json.license b/scanners/sslyze/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/sslyze/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/trivy/parser/__testFiles__/test-empty-report.json b/scanners/trivy/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/trivy/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/trivy/parser/__testFiles__/test-empty-report.json.license b/scanners/trivy/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/trivy/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/typo3scan/parser/__testFiles__/test-empty-report.json b/scanners/typo3scan/parser/__testFiles__/test-empty-report.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/scanners/typo3scan/parser/__testFiles__/test-empty-report.json @@ -0,0 +1 @@ +[] diff --git a/scanners/typo3scan/parser/__testFiles__/test-empty-report.json.license b/scanners/typo3scan/parser/__testFiles__/test-empty-report.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/typo3scan/parser/__testFiles__/test-empty-report.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 From 19228f3502c620c1c07286928f63cae901c61f70 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 4 Oct 2021 12:01:23 +0200 Subject: [PATCH 02/11] Adding empty result files and licenses Added empty test files where they were missing Signed-off-by: Sebastian --- .../__testFiles__/acstis-test-empty-findings.txt.license | 3 +++ .../git-repo-scanner/parser/__testFiles__/empty-findings.json | 2 ++ .../parser/__testFiles__/empty-findings.json.license | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 scanners/angularjs-csti-scanner/parser/__testFiles__/acstis-test-empty-findings.txt.license create mode 100644 scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json create mode 100644 scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json.license diff --git a/scanners/angularjs-csti-scanner/parser/__testFiles__/acstis-test-empty-findings.txt.license b/scanners/angularjs-csti-scanner/parser/__testFiles__/acstis-test-empty-findings.txt.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/angularjs-csti-scanner/parser/__testFiles__/acstis-test-empty-findings.txt.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 diff --git a/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json b/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json new file mode 100644 index 0000000000..0d4f101c7a --- /dev/null +++ b/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json @@ -0,0 +1,2 @@ +[ +] diff --git a/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json.license b/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json.license new file mode 100644 index 0000000000..c59b85a5b5 --- /dev/null +++ b/scanners/git-repo-scanner/parser/__testFiles__/empty-findings.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2021 iteratec GmbH + +SPDX-License-Identifier: Apache-2.0 From 044894098568be1259fb868a9cf84e8e7facf7f5 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 4 Oct 2021 12:02:54 +0200 Subject: [PATCH 03/11] Adding handling and testing of empty file to ssh-scan Check if datatype is string ==> No json data Signed-off-by: Sebastian --- scanners/ssh-scan/parser/parser.js | 3 +++ scanners/ssh-scan/parser/parser.test.js | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/scanners/ssh-scan/parser/parser.js b/scanners/ssh-scan/parser/parser.js index e7336b3c18..5c70e9e1bf 100644 --- a/scanners/ssh-scan/parser/parser.js +++ b/scanners/ssh-scan/parser/parser.js @@ -138,6 +138,9 @@ function transformRecommendationToFinding( async function parse(fileContent) { const hosts = fileContent; + if (typeof(hosts) === "string") // empty file + return []; + return hosts .flatMap(host => { if (host.error) { diff --git a/scanners/ssh-scan/parser/parser.test.js b/scanners/ssh-scan/parser/parser.test.js index ff5926d31e..68eaa7884e 100644 --- a/scanners/ssh-scan/parser/parser.test.js +++ b/scanners/ssh-scan/parser/parser.test.js @@ -272,3 +272,15 @@ test("ssh-scan parser parses a result of a network without ssh hosts correctly", await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchInlineSnapshot(`Array []`); }); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); From d60055b2dd9f0b4d268e73f637b191806515aedd Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 13:36:17 +0200 Subject: [PATCH 04/11] Removing unnecessary logging Signed-off-by: Sebastian --- scanners/angularjs-csti-scanner/parser/parser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scanners/angularjs-csti-scanner/parser/parser.js b/scanners/angularjs-csti-scanner/parser/parser.js index c6bf7d1ab5..74e4d007b8 100644 --- a/scanners/angularjs-csti-scanner/parser/parser.js +++ b/scanners/angularjs-csti-scanner/parser/parser.js @@ -43,7 +43,6 @@ function parseMethod(line) { } function parseUrl(line) { - console.log(line) return line.match(/http[s]?:\/\/[^\n ]+/g)[0] } From e1cdb72aa27ce683376bb8859eca38d5dcbd6307 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 13:38:10 +0200 Subject: [PATCH 05/11] Adding handling of empty files Either checking for undefined or if the type is string "[]" (and not an object) Signed-off-by: Sebastian --- scanners/nikto/parser/parser.js | 3 +++ scanners/sslyze/parser/parser.js | 2 +- scanners/trivy/parser/parser.js | 4 ++++ scanners/typo3scan/parser/parser.js | 4 +++- scanners/wpscan/parser/parser.js | 3 +++ 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/scanners/nikto/parser/parser.js b/scanners/nikto/parser/parser.js index f0b0c776f6..07095903ee 100644 --- a/scanners/nikto/parser/parser.js +++ b/scanners/nikto/parser/parser.js @@ -44,6 +44,9 @@ function categorize({ id }) { async function parse({ host, ip, port: portString, banner, vulnerabilities }) { const port = parseInt(portString, 10); + if (!vulnerabilities) // empty file + return []; + return vulnerabilities.filter(Boolean).map(({ id, method, url, msg }) => { const niktoId = parseInt(id, 10); diff --git a/scanners/sslyze/parser/parser.js b/scanners/sslyze/parser/parser.js index 16178952a0..9a1209d8a0 100644 --- a/scanners/sslyze/parser/parser.js +++ b/scanners/sslyze/parser/parser.js @@ -4,7 +4,7 @@ function parse(fileContent) { // Only 0 when the target wasn't reachable - if (fileContent.server_scan_results.length === 0) { + if (!fileContent.server_scan_results || fileContent.server_scan_results.length === 0) { return []; } diff --git a/scanners/trivy/parser/parser.js b/scanners/trivy/parser/parser.js index 0218f11ae4..046558545d 100644 --- a/scanners/trivy/parser/parser.js +++ b/scanners/trivy/parser/parser.js @@ -5,6 +5,10 @@ async function parse(scanResults) { // The first scan always contains the image id a similar format to: "bkimminich/juice-shop:v10.2.0 (alpine 3.11.5)" const [imageScanResult] = scanResults; + + if (typeof(imageScanResult) === "string") // empty file + return []; + const [imageId] = imageScanResult.Target.split(" ", 2); const findings = []; diff --git a/scanners/typo3scan/parser/parser.js b/scanners/typo3scan/parser/parser.js index 87916ed93f..f340e12239 100644 --- a/scanners/typo3scan/parser/parser.js +++ b/scanners/typo3scan/parser/parser.js @@ -10,6 +10,8 @@ async function parse(findings) { const domain_findings = findings[domain] const vulns = domain_findings.Vulnerabilities + if(!vulns) // empty file + return []; // Parsing Vulnerabilities const parsed_vulnerabilities = vulns.map((vuln) => { return { @@ -60,4 +62,4 @@ async function parse(findings) { }); return results } -module.exports.parse = parse; \ No newline at end of file +module.exports.parse = parse; diff --git a/scanners/wpscan/parser/parser.js b/scanners/wpscan/parser/parser.js index 284952b69d..0a1bb23147 100644 --- a/scanners/wpscan/parser/parser.js +++ b/scanners/wpscan/parser/parser.js @@ -6,6 +6,9 @@ * Convert the WPScan file / json into secureCodeBox Findings */ async function parse(scanResults) { + if (typeof(scanResults) === "string") // empty file + return []; + const wpscanVersion = scanResults.banner.version; const wpscanRequestsDone = scanResults.requests_done; From 4bf953ee385b1454e9df3d035708ea08435551d2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 13:38:30 +0200 Subject: [PATCH 06/11] Adding const to findings Signed-off-by: Sebastian --- scanners/screenshooter/parser/parser.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scanners/screenshooter/parser/parser.test.js b/scanners/screenshooter/parser/parser.test.js index dae94854f5..75f31f1ae9 100644 --- a/scanners/screenshooter/parser/parser.test.js +++ b/scanners/screenshooter/parser/parser.test.js @@ -47,7 +47,7 @@ test("should create finding correctly", async () => { test("should not create finding if image is empty", async () => { scan.spec.parameters = ["https://www.iteratec.de"]; - findings = await parse("", scan) + const findings = await parse("", scan) await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchInlineSnapshot(`Array []`); }); From 4edea4df75121012e1882302324c1525324f1409 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 13:39:03 +0200 Subject: [PATCH 07/11] Adding empty file tests where missing Signed-off-by: Sebastian --- scanners/git-repo-scanner/parser/parser.test.js | 12 ++++++++++++ scanners/kube-hunter/parser/parser.test.js | 12 ++++++++++++ scanners/kubeaudit/parser/parser.test.js | 14 +++++++++++++- scanners/nikto/parser/parser.test.js | 12 ++++++++++++ scanners/sslyze/parser/parser.test.js | 12 ++++++++++++ scanners/trivy/parser/parser.test.js | 12 ++++++++++++ scanners/typo3scan/parser/parser.test.js | 16 +++++++++++++--- scanners/wpscan/parser/parser.test.js | 12 ++++++++++++ 8 files changed, 98 insertions(+), 4 deletions(-) diff --git a/scanners/git-repo-scanner/parser/parser.test.js b/scanners/git-repo-scanner/parser/parser.test.js index ffde867b80..066ab2138f 100644 --- a/scanners/git-repo-scanner/parser/parser.test.js +++ b/scanners/git-repo-scanner/parser/parser.test.js @@ -13,6 +13,18 @@ const readFile = util.promisify(fs.readFile); const { parse } = require("./parser"); +test("should properly parse empty json file", async () => { + const fileContent = await readFile( + __dirname + "/__testFiles__/empty-findings.json", + { + encoding: "utf8", + } + ); + const findings = await parse(JSON.parse(fileContent)); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(`Array []`); +}); + test("should properly parse git-scanner json file", async () => { const fileContent = await readFile( __dirname + "/__testFiles__/git-scanner-test-findings.json", diff --git a/scanners/kube-hunter/parser/parser.test.js b/scanners/kube-hunter/parser/parser.test.js index eb58277299..2f1233c9de 100644 --- a/scanners/kube-hunter/parser/parser.test.js +++ b/scanners/kube-hunter/parser/parser.test.js @@ -26,3 +26,15 @@ test("parses result from kind-1.18-in-cluster-scan correctly", async () => { await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchSnapshot(); }); + +test("should properly parse empty kube-hunter json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); diff --git a/scanners/kubeaudit/parser/parser.test.js b/scanners/kubeaudit/parser/parser.test.js index 297cabfe30..528fd3deea 100644 --- a/scanners/kubeaudit/parser/parser.test.js +++ b/scanners/kubeaudit/parser/parser.test.js @@ -13,7 +13,7 @@ const readFile = util.promisify(fs.readFile); const { parse } = require("./parser"); -test("example parser parses empty json to zero findings", async () => { +test("should properly parse kubeaudit juice-shop results", async () => { const fileContent = await readFile( __dirname + "/__testFiles__/juice-shop.jsonl", { @@ -25,3 +25,15 @@ test("example parser parses empty json to zero findings", async () => { expect(findings).toMatchSnapshot(); }); + +test("should properly parse empty kubeaudit jsonl file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.jsonl", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); diff --git a/scanners/nikto/parser/parser.test.js b/scanners/nikto/parser/parser.test.js index 7bfe10e237..1e16e5179f 100644 --- a/scanners/nikto/parser/parser.test.js +++ b/scanners/nikto/parser/parser.test.js @@ -34,3 +34,15 @@ test("parses OWASP Juice Shop result file into findings", async () => { await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchSnapshot(); }); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); diff --git a/scanners/sslyze/parser/parser.test.js b/scanners/sslyze/parser/parser.test.js index 9768619c15..9f905f4325 100644 --- a/scanners/sslyze/parser/parser.test.js +++ b/scanners/sslyze/parser/parser.test.js @@ -423,3 +423,15 @@ test("parses an result file with mixed connectivity correctly", async () => { await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toEqual([]); }); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); diff --git a/scanners/trivy/parser/parser.test.js b/scanners/trivy/parser/parser.test.js index 83e43bb058..4418e9fdf9 100644 --- a/scanners/trivy/parser/parser.test.js +++ b/scanners/trivy/parser/parser.test.js @@ -85,3 +85,15 @@ test("parses mediawiki:1.27.3 result file into findings", async () => { await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchSnapshot(); }); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); diff --git a/scanners/typo3scan/parser/parser.test.js b/scanners/typo3scan/parser/parser.test.js index 23737e38d4..b450621275 100644 --- a/scanners/typo3scan/parser/parser.test.js +++ b/scanners/typo3scan/parser/parser.test.js @@ -20,7 +20,6 @@ test("parser parses large json result without vulnerable extensions successfully encoding: "utf8", } ); - const findings = await parse(JSON.parse(fileContent)); await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchSnapshot(); @@ -33,8 +32,19 @@ test("parser parses large json result with vulnerable extensions successfully", encoding: "utf8", } ); - const findings = await parse(JSON.parse(fileContent)); await expect(validateParser(findings)).resolves.toBeUndefined(); expect(findings).toMatchSnapshot(); -}); \ No newline at end of file +}); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/test-empty-report.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot(`Array []`); +}); diff --git a/scanners/wpscan/parser/parser.test.js b/scanners/wpscan/parser/parser.test.js index fec9160e25..3c4067651f 100644 --- a/scanners/wpscan/parser/parser.test.js +++ b/scanners/wpscan/parser/parser.test.js @@ -262,3 +262,15 @@ test("WPScan parser parses a scan result file without a detected wp version corr ] `); }); + +test("should properly parse empty json file", async () => { + const jsonContent = await readFile( + __dirname + "/__testFiles__/empty-localhost.json", + { + encoding: "utf8", + } + ); + const findings = await parse(jsonContent); + await expect(validateParser(findings)).resolves.toBeUndefined(); + expect(findings).toMatchInlineSnapshot("Array []"); +}); From f31f8345954a96bab86cebb98fe2103b85b68282 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 13:46:31 +0200 Subject: [PATCH 08/11] Updating snapshot for kubeaudit juiceshop test Signed-off-by: Sebastian --- .../parser/__snapshots__/parser.test.js.snap | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap b/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap index ab3b0171b7..7cdcb29673 100644 --- a/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap +++ b/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap @@ -214,3 +214,218 @@ Array [ }, ] `; + +exports[`should properly parse kubeaudit juice-shop results 1`] = ` +Array [ + Object { + "attributes": Object {}, + "category": "Automounted ServiceAccount Token", + "description": "Default service account with token mounted. automountServiceAccountToken should be set to 'false' on either the ServiceAccount or on the PodSpec or a non-default service account should be used.", + "location": null, + "name": "Default ServiceAccount uses Automounted Service Account Token", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "AUDIT_WRITE", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'AUDIT_WRITE' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "CHOWN", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'CHOWN' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "DAC_OVERRIDE", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'DAC_OVERRIDE' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "FOWNER", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'FOWNER' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "FSETID", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'FSETID' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "KILL", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'KILL' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "MKNOD", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'MKNOD' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "NET_BIND_SERVICE", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'NET_BIND_SERVICE' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "NET_RAW", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'NET_RAW' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "SETFCAP", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'SETFCAP' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "SETGID", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'SETGID' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "SETPCAP", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'SETPCAP' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "SETUID", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'SETUID' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "capability": "SYS_CHROOT", + "container": "juice-shop", + }, + "category": "Capability Not Dropped", + "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", + "location": "container://juice-shop", + "name": "Capability 'SYS_CHROOT' Not Dropped", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "container": "juice-shop", + }, + "category": "Non Root User Not Enforced", + "description": "runAsNonRoot is not set in container SecurityContext nor the PodSecurityContext. It should be set to 'true' in at least one of the two.", + "location": "container://juice-shop", + "name": "NonRoot User not enforced for Container", + "osi_layer": "NOT_APPLICABLE", + "severity": "MEDIUM", + }, + Object { + "attributes": Object { + "container": "juice-shop", + }, + "category": "Non ReadOnly Root Filesystem", + "description": "readOnlyRootFilesystem is not set in container SecurityContext. It should be set to 'true'.", + "location": "container://juice-shop", + "name": "Container Uses a non ReadOnly Root Filesystem", + "osi_layer": "NOT_APPLICABLE", + "severity": "LOW", + }, + Object { + "attributes": Object { + "Namespace": "default", + }, + "category": "No Default Deny NetworkPolicy", + "description": "Namespace is missing a default deny ingress and egress NetworkPolicy.", + "location": "namespace://default", + "name": "Namespace \\"default\\" is missing a Default Deny NetworkPolicy", + "osi_layer": "NOT_APPLICABLE", + "severity": "MEDIUM", + }, +] +`; From c123074d25bbde14520dad6689577623319f6302 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 14:17:28 +0200 Subject: [PATCH 09/11] Updating snapshot name Signed-off-by: Sebastian --- .../parser/__snapshots__/parser.test.js.snap | 215 ------------------ 1 file changed, 215 deletions(-) diff --git a/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap b/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap index 7cdcb29673..1c08ee311f 100644 --- a/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap +++ b/scanners/kubeaudit/parser/__snapshots__/parser.test.js.snap @@ -1,220 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`example parser parses empty json to zero findings 1`] = ` -Array [ - Object { - "attributes": Object {}, - "category": "Automounted ServiceAccount Token", - "description": "Default service account with token mounted. automountServiceAccountToken should be set to 'false' on either the ServiceAccount or on the PodSpec or a non-default service account should be used.", - "location": null, - "name": "Default ServiceAccount uses Automounted Service Account Token", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "AUDIT_WRITE", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'AUDIT_WRITE' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "CHOWN", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'CHOWN' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "DAC_OVERRIDE", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'DAC_OVERRIDE' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "FOWNER", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'FOWNER' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "FSETID", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'FSETID' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "KILL", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'KILL' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "MKNOD", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'MKNOD' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "NET_BIND_SERVICE", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'NET_BIND_SERVICE' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "NET_RAW", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'NET_RAW' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "SETFCAP", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'SETFCAP' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "SETGID", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'SETGID' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "SETPCAP", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'SETPCAP' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "SETUID", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'SETUID' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "capability": "SYS_CHROOT", - "container": "juice-shop", - }, - "category": "Capability Not Dropped", - "description": "Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.", - "location": "container://juice-shop", - "name": "Capability 'SYS_CHROOT' Not Dropped", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "container": "juice-shop", - }, - "category": "Non Root User Not Enforced", - "description": "runAsNonRoot is not set in container SecurityContext nor the PodSecurityContext. It should be set to 'true' in at least one of the two.", - "location": "container://juice-shop", - "name": "NonRoot User not enforced for Container", - "osi_layer": "NOT_APPLICABLE", - "severity": "MEDIUM", - }, - Object { - "attributes": Object { - "container": "juice-shop", - }, - "category": "Non ReadOnly Root Filesystem", - "description": "readOnlyRootFilesystem is not set in container SecurityContext. It should be set to 'true'.", - "location": "container://juice-shop", - "name": "Container Uses a non ReadOnly Root Filesystem", - "osi_layer": "NOT_APPLICABLE", - "severity": "LOW", - }, - Object { - "attributes": Object { - "Namespace": "default", - }, - "category": "No Default Deny NetworkPolicy", - "description": "Namespace is missing a default deny ingress and egress NetworkPolicy.", - "location": "namespace://default", - "name": "Namespace \\"default\\" is missing a Default Deny NetworkPolicy", - "osi_layer": "NOT_APPLICABLE", - "severity": "MEDIUM", - }, -] -`; - exports[`should properly parse kubeaudit juice-shop results 1`] = ` Array [ Object { From 971a573bcf8fe9711c93a5a1d10760799642809a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 5 Oct 2021 15:33:11 +0200 Subject: [PATCH 10/11] Making codeclimate happy Signed-off-by: Sebastian --- scanners/trivy/parser/parser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scanners/trivy/parser/parser.js b/scanners/trivy/parser/parser.js index 046558545d..2be734a659 100644 --- a/scanners/trivy/parser/parser.js +++ b/scanners/trivy/parser/parser.js @@ -5,7 +5,6 @@ async function parse(scanResults) { // The first scan always contains the image id a similar format to: "bkimminich/juice-shop:v10.2.0 (alpine 3.11.5)" const [imageScanResult] = scanResults; - if (typeof(imageScanResult) === "string") // empty file return []; From 67e7fb0b30826cf338515a306a808c8a20bf9086 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 12 Oct 2021 12:09:01 +0200 Subject: [PATCH 11/11] Extracting category detection to a separate function This makes the function shorter and clearer (and codeclimate happy) Signed-off-by: Sebastian --- scanners/trivy/parser/parser.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/scanners/trivy/parser/parser.js b/scanners/trivy/parser/parser.js index 2be734a659..5a09d75212 100644 --- a/scanners/trivy/parser/parser.js +++ b/scanners/trivy/parser/parser.js @@ -5,6 +5,7 @@ async function parse(scanResults) { // The first scan always contains the image id a similar format to: "bkimminich/juice-shop:v10.2.0 (alpine 3.11.5)" const [imageScanResult] = scanResults; + if (typeof(imageScanResult) === "string") // empty file return []; @@ -14,18 +15,7 @@ async function parse(scanResults) { for (const { Target: target, Vulnerabilities } of scanResults) { const vulnerabilities = Vulnerabilities || []; - let category = "Image Vulnerability"; - if (target.endsWith("package-lock.json")) { - category = "NPM Package Vulnerability"; - } else if (target.endsWith("Gemfile.lock")) { - category = "Ruby Package Vulnerability"; - } else if (target.endsWith("Pipfile.lock")) { - category = "Python Package Vulnerability"; - } else if (target.endsWith("Cargo.lock")) { - category = "Python Package Vulnerability"; - } else if (target.endsWith("Composer.lock")) { - category = "PHP Package Vulnerability"; - } + const category = getCategory(target); for (const vulnerability of vulnerabilities) { let reference = null; @@ -60,10 +50,25 @@ async function parse(scanResults) { }); } } - return findings; } +function getCategory(target) { + let category = "Image Vulnerability"; + if (target.endsWith("package-lock.json")) { + category = "NPM Package Vulnerability"; + } else if (target.endsWith("Gemfile.lock")) { + category = "Ruby Package Vulnerability"; + } else if (target.endsWith("Pipfile.lock")) { + category = "Python Package Vulnerability"; + } else if (target.endsWith("Cargo.lock")) { + category = "Python Package Vulnerability"; + } else if (target.endsWith("Composer.lock")) { + category = "PHP Package Vulnerability"; + } + return category; +} + function getAdjustedSeverity(severity){ return severity === "CRITICAL" ? "HIGH"