From b45b6b198b3b375f60d59da6455e1cdae9cc0667 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 12 May 2022 16:32:47 -0400 Subject: [PATCH] Improved unit test coverage of new analyzers; Utilize localized summaries; Require 100% code coverage on analyzers --- .github/workflows/pythontest.yml | 2 +- .../files/analyzers/emailrep/emailrep.py | 4 ++-- .../files/analyzers/emailrep/emailrep_test.py | 4 ++-- .../files/analyzers/greynoise/greynoise.py | 4 ++-- .../files/analyzers/greynoise/greynoise_test.py | 6 +++--- salt/sensoroni/files/analyzers/ja3er/ja3er.py | 6 +++--- .../sensoroni/files/analyzers/ja3er/ja3er_test.py | 15 +++++++++++---- .../files/analyzers/localfile/localfile.py | 2 +- .../files/analyzers/localfile/localfile_test.py | 4 ++-- salt/sensoroni/files/analyzers/otx/otx.py | 4 ++-- salt/sensoroni/files/analyzers/otx/otx_test.py | 6 +++--- salt/sensoroni/files/analyzers/urlscan/urlscan.py | 2 +- .../files/analyzers/urlscan/urlscan_test.py | 4 ++-- .../files/analyzers/virustotal/virustotal_test.py | 2 +- 14 files changed, 36 insertions(+), 29 deletions(-) diff --git a/.github/workflows/pythontest.yml b/.github/workflows/pythontest.yml index c0e692730..241245044 100644 --- a/.github/workflows/pythontest.yml +++ b/.github/workflows/pythontest.yml @@ -28,4 +28,4 @@ jobs: flake8 ${{ matrix.python-code-path }} --show-source --max-complexity=12 --doctests --max-line-length=200 --statistics - name: Test with pytest run: | - pytest ${{ matrix.python-code-path }} --cov=${{ matrix.python-code-path }} --doctest-modules --cov-report=term --cov-fail-under=90 --cov-config=${{ matrix.python-code-path }}/pytest.ini + pytest ${{ matrix.python-code-path }} --cov=${{ matrix.python-code-path }} --doctest-modules --cov-report=term --cov-fail-under=100 --cov-config=${{ matrix.python-code-path }}/pytest.ini diff --git a/salt/sensoroni/files/analyzers/emailrep/emailrep.py b/salt/sensoroni/files/analyzers/emailrep/emailrep.py index 4e9a8fee9..d48977a07 100755 --- a/salt/sensoroni/files/analyzers/emailrep/emailrep.py +++ b/salt/sensoroni/files/analyzers/emailrep/emailrep.py @@ -31,10 +31,10 @@ def prepareResults(raw): elif "status" in raw: if raw["reason"] == "invalid email": status = "caution" - summary = "Invalid email address." + summary = "invalid_input" if "exceeded daily limit" in raw["reason"]: status = "caution" - summary = "Exceeded daily request limit." + summary = "excessive_usage" else: status = "caution" summary = "internal_failure" diff --git a/salt/sensoroni/files/analyzers/emailrep/emailrep_test.py b/salt/sensoroni/files/analyzers/emailrep/emailrep_test.py index ecd6010b0..283aef694 100644 --- a/salt/sensoroni/files/analyzers/emailrep/emailrep_test.py +++ b/salt/sensoroni/files/analyzers/emailrep/emailrep_test.py @@ -44,7 +44,7 @@ class TestEmailRepMethods(unittest.TestCase): raw = {"status": "fail", "reason": "invalid email"} results = emailrep.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Invalid email address.") + self.assertEqual(results["summary"], "invalid_input") self.assertEqual(results["status"], "caution") def test_prepareResults_not_suspicious(self): @@ -65,7 +65,7 @@ class TestEmailRepMethods(unittest.TestCase): raw = {"status": "fail", "reason": "exceeded daily limit. please wait 24 hrs or visit emailrep.io/key for an api key."} results = emailrep.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Exceeded daily request limit.") + self.assertEqual(results["summary"], "excessive_usage") self.assertEqual(results["status"], "caution") def test_prepareResults_error(self): diff --git a/salt/sensoroni/files/analyzers/greynoise/greynoise.py b/salt/sensoroni/files/analyzers/greynoise/greynoise.py index deeef5414..bf2b98e7a 100755 --- a/salt/sensoroni/files/analyzers/greynoise/greynoise.py +++ b/salt/sensoroni/files/analyzers/greynoise/greynoise.py @@ -36,13 +36,13 @@ def prepareResults(raw): summary = "malicious" elif "unknown" in raw['classification']: status = "caution" - summary = "Results found." + summary = "suspicious" elif "IP not observed scanning the internet or contained in RIOT data set." in raw["message"]: status = "ok" summary = "no_results" elif "Request is not a valid routable IPv4 address" in raw["message"]: status = "caution" - summary = "Invalid IP address." + summary = "invalid_input" else: status = "info" summary = raw["message"] diff --git a/salt/sensoroni/files/analyzers/greynoise/greynoise_test.py b/salt/sensoroni/files/analyzers/greynoise/greynoise_test.py index 768107adb..900a35e8c 100644 --- a/salt/sensoroni/files/analyzers/greynoise/greynoise_test.py +++ b/salt/sensoroni/files/analyzers/greynoise/greynoise_test.py @@ -62,7 +62,7 @@ class TestGreynoiseMethods(unittest.TestCase): raw = {"message": "Request is not a valid routable IPv4 address"} results = greynoise.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Invalid IP address.") + self.assertEqual(results["summary"], "invalid_input") self.assertEqual(results["status"], "caution") def test_prepareResults_not_found(self): @@ -90,7 +90,7 @@ class TestGreynoiseMethods(unittest.TestCase): raw = {"ip": "221.4.62.149", "noise": "true", "riot": "false", "classification": "unknown", "name": "unknown", "link": "https://viz.gn.io", "last_seen": "2022-04-26", "message": "Success"} results = greynoise.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Results found.") + self.assertEqual(results["summary"], "suspicious") self.assertEqual(results["status"], "caution") def test_prepareResults_unknown_message(self): @@ -113,5 +113,5 @@ class TestGreynoiseMethods(unittest.TestCase): conf = {"base_url": "myurl/", "api_key": "abcd1234", "api_version": "community"} with patch('greynoise.greynoise.sendReq', new=MagicMock(return_value=output)) as mock: results = greynoise.analyze(conf, artifactInput) - self.assertEqual(results["summary"], "Results found.") + self.assertEqual(results["summary"], "suspicious") mock.assert_called_once() diff --git a/salt/sensoroni/files/analyzers/ja3er/ja3er.py b/salt/sensoroni/files/analyzers/ja3er/ja3er.py index 330a8dd66..c1018a880 100755 --- a/salt/sensoroni/files/analyzers/ja3er/ja3er.py +++ b/salt/sensoroni/files/analyzers/ja3er/ja3er.py @@ -15,16 +15,16 @@ def prepareResults(raw): if "error" in raw: if "Sorry" in raw["error"]: status = "ok" - summary = "No results found." + summary = "no_results" elif "Invalid hash" in raw["error"]: status = "caution" - summary = "Invalid hash." + summary = "invalid_input" else: status = "caution" summary = "internal_failure" else: status = "info" - summary = "Results found." + summary = "suspicious" results = {'response': raw, 'summary': summary, 'status': status} return results diff --git a/salt/sensoroni/files/analyzers/ja3er/ja3er_test.py b/salt/sensoroni/files/analyzers/ja3er/ja3er_test.py index 8ad22ac69..41de4e9c7 100644 --- a/salt/sensoroni/files/analyzers/ja3er/ja3er_test.py +++ b/salt/sensoroni/files/analyzers/ja3er/ja3er_test.py @@ -38,21 +38,28 @@ class TestJa3erMethods(unittest.TestCase): raw = {"error": "Sorry no values found"} results = ja3er.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "No results found.") + self.assertEqual(results["summary"], "no_results") self.assertEqual(results["status"], "ok") def test_prepareResults_invalidHash(self): raw = {"error": "Invalid hash"} results = ja3er.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Invalid hash.") + self.assertEqual(results["summary"], "invalid_input") + self.assertEqual(results["status"], "caution") + + def test_prepareResults_internal_failure(self): + raw = {"error": "unknown"} + results = ja3er.prepareResults(raw) + self.assertEqual(results["response"], raw) + self.assertEqual(results["summary"], "internal_failure") self.assertEqual(results["status"], "caution") def test_prepareResults_info(self): raw = [{"User-Agent": "Blah/5.0", "Count": 24874, "Last_seen": "2022-04-08 16:18:38"}, {"Comment": "Brave browser v1.36.122\n\n", "Reported": "2022-03-28 20:26:42"}] results = ja3er.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Results found.") + self.assertEqual(results["summary"], "suspicious") self.assertEqual(results["status"], "info") def test_analyze(self): @@ -61,5 +68,5 @@ class TestJa3erMethods(unittest.TestCase): conf = {"base_url": "myurl/"} with patch('ja3er.ja3er.sendReq', new=MagicMock(return_value=output)) as mock: results = ja3er.analyze(conf, artifactInput) - self.assertEqual(results["summary"], "Results found.") + self.assertEqual(results["summary"], "suspicious") mock.assert_called_once() diff --git a/salt/sensoroni/files/analyzers/localfile/localfile.py b/salt/sensoroni/files/analyzers/localfile/localfile.py index 745c4b9b6..5538d6a93 100755 --- a/salt/sensoroni/files/analyzers/localfile/localfile.py +++ b/salt/sensoroni/files/analyzers/localfile/localfile.py @@ -43,7 +43,7 @@ def prepareResults(raw): summary = "no_results" else: status = "info" - summary = "One or more matches found." + summary = "suspicious" else: raw = {} status = "caution" diff --git a/salt/sensoroni/files/analyzers/localfile/localfile_test.py b/salt/sensoroni/files/analyzers/localfile/localfile_test.py index 66e4820e1..30b171f86 100644 --- a/salt/sensoroni/files/analyzers/localfile/localfile_test.py +++ b/salt/sensoroni/files/analyzers/localfile/localfile_test.py @@ -86,7 +86,7 @@ class TestLocalfileMethods(unittest.TestCase): ] results = localfile.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "One or more matches found.") + self.assertEqual(results["summary"], "suspicious") self.assertEqual(results["status"], "info") def test_prepareResults_error(self): @@ -115,5 +115,5 @@ class TestLocalfileMethods(unittest.TestCase): conf = {"file_path": "/home/intel.csv"} with patch('localfile.localfile.searchFile', new=MagicMock(return_value=output)) as mock: results = localfile.analyze(conf, artifactInput) - self.assertEqual(results["summary"], "One or more matches found.") + self.assertEqual(results["summary"], "suspicious") mock.assert_called_once() diff --git a/salt/sensoroni/files/analyzers/otx/otx.py b/salt/sensoroni/files/analyzers/otx/otx.py index 2d4e8e592..b9565058f 100755 --- a/salt/sensoroni/files/analyzers/otx/otx.py +++ b/salt/sensoroni/files/analyzers/otx/otx.py @@ -44,7 +44,7 @@ def prepareResults(response): summaryinfo = "harmless" elif reputation > 0 and reputation < 50: status = "ok" - summaryinfo = "Likely Harmless" + summaryinfo = "likely_harmless" elif reputation >= 50 and reputation < 75: status = "caution" summaryinfo = "suspicious" @@ -53,7 +53,7 @@ def prepareResults(response): summaryinfo = "malicious" else: status = "info" - summaryinfo = "Analysis complete." + summaryinfo = "analyzer_analysis_complete" else: raw = {} status = "caution" diff --git a/salt/sensoroni/files/analyzers/otx/otx_test.py b/salt/sensoroni/files/analyzers/otx/otx_test.py index 6f5764ca4..3e48168a9 100644 --- a/salt/sensoroni/files/analyzers/otx/otx_test.py +++ b/salt/sensoroni/files/analyzers/otx/otx_test.py @@ -25,7 +25,7 @@ class TestOtxMethods(unittest.TestCase): self.assertEqual(mock_stdout.getvalue(), expected) mock.assert_called_once() - def checkConfigRequirements(self): + def test_checkConfigRequirements(self): conf = {"not_a_key": "abcd12345"} with self.assertRaises(SystemExit) as cm: otx.checkConfigRequirements(conf) @@ -119,7 +119,7 @@ class TestOtxMethods(unittest.TestCase): } results = otx.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Likely Harmless") + self.assertEqual(results["summary"], "likely_harmless") self.assertEqual(results["status"], "ok") def test_prepareResults_suspicious(self): @@ -210,7 +210,7 @@ class TestOtxMethods(unittest.TestCase): } results = otx.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Analysis complete.") + self.assertEqual(results["summary"], "analyzer_analysis_complete") self.assertEqual(results["status"], "info") def test_prepareResults_error(self): diff --git a/salt/sensoroni/files/analyzers/urlscan/urlscan.py b/salt/sensoroni/files/analyzers/urlscan/urlscan.py index 94c3ec8db..a07e61c89 100755 --- a/salt/sensoroni/files/analyzers/urlscan/urlscan.py +++ b/salt/sensoroni/files/analyzers/urlscan/urlscan.py @@ -54,7 +54,7 @@ def prepareResults(raw): summary = "suspicious" else: status = "info" - summary = "Scan complete." + summary = "analysis_complete" else: status = "caution" summary = "internal_failure" diff --git a/salt/sensoroni/files/analyzers/urlscan/urlscan_test.py b/salt/sensoroni/files/analyzers/urlscan/urlscan_test.py index 487e6dbe3..5135c3cd2 100644 --- a/salt/sensoroni/files/analyzers/urlscan/urlscan_test.py +++ b/salt/sensoroni/files/analyzers/urlscan/urlscan_test.py @@ -96,7 +96,7 @@ class TestUrlScanMethods(unittest.TestCase): raw = {"requests": [{"request": {"requestId": "3"}}], "verdicts": {"overall": {"score": 0, "malicious": False, "hasVerdicts": False}}} results = urlscan.prepareResults(raw) self.assertEqual(results["response"], raw) - self.assertEqual(results["summary"], "Scan complete.") + self.assertEqual(results["summary"], "analysis_complete") self.assertEqual(results["status"], "info") def test_prepareResults_error(self): @@ -116,6 +116,6 @@ class TestUrlScanMethods(unittest.TestCase): with patch('urlscan.urlscan.sendReq', new=MagicMock(return_value=output_req)) as mock_req: with patch('urlscan.urlscan.getReport', new=MagicMock(return_value=output_report)) as mock_report: results = urlscan.analyze(conf, artifactInput) - self.assertEqual(results["summary"], "Scan complete.") + self.assertEqual(results["summary"], "analysis_complete") mock_req.assert_called_once() mock_report.assert_called_once() diff --git a/salt/sensoroni/files/analyzers/virustotal/virustotal_test.py b/salt/sensoroni/files/analyzers/virustotal/virustotal_test.py index 103e137ce..177c16d99 100644 --- a/salt/sensoroni/files/analyzers/virustotal/virustotal_test.py +++ b/salt/sensoroni/files/analyzers/virustotal/virustotal_test.py @@ -25,7 +25,7 @@ class TestVirusTotalMethods(unittest.TestCase): self.assertEqual(mock_stdout.getvalue(), expected) mock.assert_called_once() - def checkConfigRequirements(self): + def test_checkConfigRequirements(self): conf = {"not_a_key": "abcd12345"} with self.assertRaises(SystemExit) as cm: virustotal.checkConfigRequirements(conf)