diff --git a/salt/sensoroni/files/analyzers/echotrail/echotrail_test.py b/salt/sensoroni/files/analyzers/echotrail/echotrail_test.py index b6873c507..ea0d7433a 100644 --- a/salt/sensoroni/files/analyzers/echotrail/echotrail_test.py +++ b/salt/sensoroni/files/analyzers/echotrail/echotrail_test.py @@ -49,6 +49,20 @@ class TestEchoTrailMethods(unittest.TestCase): results = echotrail.prepareResults(raw) self.assertEqual(results, sim_results) + def test_prepareResults_filenames(self): + raw = {'filenames': [["abc.exe", "def.exe"], ["abc.exe", "def.exe"]]} + sim_results = {'response': raw, + 'status': 'info', 'summary': 'abc.exe'} + results = echotrail.prepareResults(raw) + self.assertEqual(results, sim_results) + + def test_prepareResults_tags(self): + raw = {'tags': [["tag1", "tag2"], ["tag1", "tag2"]]} + sim_results = {'response': raw, + 'status': 'info', 'summary': 'tag1'} + results = echotrail.prepareResults(raw) + self.assertEqual(results, sim_results) + def test_analyze(self): sendReqOutput = {'threat': 'no_result'} input = '{"artifactType":"hash", "value":"1234"}' diff --git a/salt/sensoroni/files/analyzers/elasticsearch/elasticsearch_test.py b/salt/sensoroni/files/analyzers/elasticsearch/elasticsearch_test.py index ba521ca81..902b71fa8 100644 --- a/salt/sensoroni/files/analyzers/elasticsearch/elasticsearch_test.py +++ b/salt/sensoroni/files/analyzers/elasticsearch/elasticsearch_test.py @@ -32,13 +32,70 @@ class TestElasticSearchMethods(unittest.TestCase): mock_yaml.assert_called_once() '''Test that checks for empty and none values in configurables''' - def test_checkConfigRequirements(self): - conf = {"base_url": "", "auth_user": "", "auth_pwd": "", "num_results": None, "api_key": "", "index": "", "time_delta_minutes": None, "timestamp_field_name": "", "map": {}, "cert_path": ""} + def test_checkConfigRequirements_no_num_results(self): + conf = {"base_url": "https://baseurl", "auth_user": "test", + "auth_pwd": "test", "num_results": None, "api_key": "abcd1234", + "index": "_all", "time_delta_minutes": 12345, "timestamp_field_name": "@timestamp", + "map": {"test": "test"}, "cert_path": "/cert"} with self.assertRaises(SystemExit) as cm: elasticsearch.checkConfigRequirements(conf) self.assertEqual(cm.exception.code, 126) + + def test_checkConfigRequirements_no_delta(self): + conf = {"base_url": "https://baseurl", "auth_user": "test", + "auth_pwd": "test", "num_results": 1, "api_key": "abcd1234", + "index": "_all", "time_delta_minutes": None, "timestamp_field_name": "@timestamp", + "map": {"test": "test"}, "cert_path": "/cert"} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + + def test_checkConfigRequirements_no_auth_user(self): + conf = {"base_url": "https://baseurl", "auth_user": None, "auth_pwd": "test", + "num_results": "1", "api_key": None, "index": "_all", "time_delta_minutes": 12345, + "timestamp_field_name": "@timestamp", "map": {"test": "test"}, "cert_path": "/cert"} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + '''Test that checks buildReq method, by comparing a mock buildReq result with an expectedQuery, used a mock object to simulate an expectedQuery since Elasticsearch buildReq uses values in the config''' + + def test_checkConfigRequirements_no_index(self): + conf = {"base_url": "https://baseurl", "auth_user": "test", "auth_pwd": "test", + "num_results": "1", "api_key": "abcd1234", "index": None, "time_delta_minutes": 12345, + "timestamp_field_name": "@timestamp", "map": {"test": "test"}, "cert_path": "/cert"} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + + def test_checkConfigRequirements_no_base_url(self): + conf = {"base_url": None, "auth_user": "test", "auth_pwd": "test", "num_results": "1", + "api_key": "abcd1234", "index": "_all", "time_delta_minutes": 12345, + "timestamp_field_name": "@timestamp", "map": {"test": "test"}, "cert_path": "/cert"} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + + def test_checkConfigRequirements_no_timestamp_field_name(self): + conf = {"base_url": "https://baseurl", "auth_user": "test", "auth_pwd": "test", "num_results": "1", + "api_key": "abcd1234", "index": "_all", "time_delta_minutes": 12345, + "timestamp_field_name": None, "map": {"test": "test"}, "cert_path": "/cert"} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + + def test_checkConfigRequirements_no_cert_path(self): + conf = {"base_url": "https://baseurl", "auth_user": "test", "auth_pwd": "test", "num_results": "1", + "api_key": "abcd1234", "index": "_all", "time_delta_minutes": 12345, "timestamp_field_name": "@timestamp", + "map": {"test": "test"}, "cert_path": None} + with self.assertRaises(SystemExit) as cm: + elasticsearch.checkConfigRequirements(conf) + self.assertEqual(cm.exception.code, 126) + + '''Test that checks buildReq method, by comparing a mock buildReq result with an expectedQuery, used a mock object to simulate an expectedQuery + since Elasticsearch buildReq uses values in the config''' + def test_buildReq(self): numberOfResults = 1 observableType = "hash" @@ -70,7 +127,8 @@ class TestElasticSearchMethods(unittest.TestCase): mock.assert_called_once() def test_wrongbuildReq(self): - result = {'map': '123', 'artifactType': 'hash', 'timestamp_field_name': 'abc', 'time_delta_minutes': 14400, 'num_results': 10, 'value': '0'} + mapping = None + result = {'map': mapping, 'artifactType': 'hash', 'timestamp_field_name': 'abc', 'time_delta_minutes': 14400, 'num_results': 10, 'value': '0'} cur_time = datetime.now() start_time = cur_time - timedelta(minutes=result['time_delta_minutes']) query = elasticsearch.buildReq(result, result) @@ -155,24 +213,28 @@ class TestElasticSearchMethods(unittest.TestCase): self.assertEqual(query, comparequery) '''Test that checks sendReq method to expect a response from a requests.post''' - def test_sendReq(self): + def test_sendReq_user_password(self): conf = {"base_url": "test", "auth_user": "test", "auth_pwd": "test", "api_key": "test", "index": "test", "cert_path": ""} with patch('requests.post', new=MagicMock(return_value=MagicMock())) as mock: response = elasticsearch.sendReq(conf, 'example_query') self.assertIsNotNone(response) mock.assert_called_once + def test_sendReq_apikey(self): + conf = {"base_url": "test", "auth_user": None, "auth_pwd": None, "api_key": "abcd1234", "index": "test", "cert_path": ""} + with patch('requests.post', new=MagicMock(return_value=MagicMock())) as mock: + response = elasticsearch.sendReq(conf, 'example_query') + self.assertIsNotNone(response) + mock.assert_called_once + '''Test that checks prepareResults method, by comparing a mock prepareResults return_value with an expectedResult''' def test_prepareResults(self): - summary = "Documents returned: 5" - status = 'info' - raw = {'_id': "0", "hash": "123"} - expectedResult = {'response': raw, 'summary': summary, 'status': status} + raw = {"hits": {"hits": [{"_id": 0, "hash": "123"}]}} + results = elasticsearch.prepareResults(raw) + self.assertEqual(results["response"], raw) + self.assertEqual(results["summary"], "Documents returned: 1") + self.assertEqual(results["status"], "info") - with patch('elasticsearch.prepareResults', new=MagicMock(return_value=expectedResult)) as mock: - response = elasticsearch.prepareResults(raw) - self.assertEqual(expectedResult, response) - mock.assert_called_once() '''Test that checks analyze method, simulated sendReq and prepareResults with 2 mock objects and variables sendReqOutput and prepareResultOutput, input created for analyze method call and then we compared results['summary'] with 'Documents returned: 5' ''' def test_analyze(self): diff --git a/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar.py b/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar.py index 7ec484338..8a8f51f73 100755 --- a/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar.py +++ b/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar.py @@ -65,11 +65,11 @@ def prepareResults(raw): vendor_data = parsed['vendor_intel'] # get summary - if parsed['signature']: + if 'signature' in parsed: summary = parsed['signature'] - elif parsed['tags']: + elif 'tags' in parsed: summary = str(parsed['tags'][0]) - elif vendor_data['YOROI_YOMI']: + elif 'YOROI_YOMI' in vendor_data: summary = vendor_data['YOROI_YOMI']['detection'] # gauge vendors to determine an approximation of status, normalized to a value out of 100 @@ -92,7 +92,7 @@ def prepareResults(raw): score = max(score, 0) # compute status - if score >= 75 or isInJson(raw, 'MALICIOUS'.lower()): + if score >= 75 or isInJson(raw, 'MALICIOUS'.lower(), 1001): # if score >= 75: status = 'threat' elif score >= 50: diff --git a/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar_test.py b/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar_test.py index 1c824e538..edaff0125 100644 --- a/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar_test.py +++ b/salt/sensoroni/files/analyzers/malwarebazaar/malwarebazaar_test.py @@ -46,6 +46,30 @@ class TestMalwarebazaarMethods(unittest.TestCase): mock2.assert_called() mock.assert_called() + def test_analyze_result(self): + """simulated sendReq and prepareResults with 2 mock objects and variables sendReqOutput and prepareResultOutput, + input created for analyze method call and then we compared results['summary'] with 'no result' """ + sendReqOutput = {'threat': 'threat', "query_status": "notok", 'data': [{'sha256_hash': 'validhash'}]} + input = '{"artifactType": "hash", "value": "1234"}' + input2 = '{"artifactType": "tlsh", "value": "1234"}' + input3 = '{"artifactType": "gimphash", "value": "1234"}' + prepareResultOutput = {'response': '', + 'summary': 'Bad', 'status': 'threat'} + + with patch('malwarebazaar.malwarebazaar.sendReq', new=MagicMock(return_value=sendReqOutput)) as mock: + with patch('malwarebazaar.malwarebazaar.prepareResults', new=MagicMock(return_value=prepareResultOutput)) as mock2: + results = malwarebazaar.analyze(input) + results2 = malwarebazaar.analyze(input2) + results3 = malwarebazaar.analyze(input3) + self.assertEqual(results["summary"], prepareResultOutput['summary']) + self.assertEqual(results2["summary"], prepareResultOutput['summary']) + self.assertEqual(results3["summary"], prepareResultOutput['summary']) + self.assertEqual(results["status"], "threat") + self.assertEqual(results2["status"], "threat") + self.assertEqual(results3["status"], "threat") + mock2.assert_called() + mock.assert_called() + def test_prepareResults_illegal_search_term(self): # illegal search term raw = {'query_status': 'illegal_search_term'} @@ -53,6 +77,54 @@ class TestMalwarebazaarMethods(unittest.TestCase): results = malwarebazaar.prepareResults(raw) self.assertEqual(results, expected) + def test_prepareResults_empty(self): + # raw is empty + raw = {} + expected = {'response': raw, 'status': 'caution', 'summary': 'internal_failure'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_threat(self): + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'ReversingLabs': {'status': 'MALICIOUS'}}, 'signature': 'abcd1234', 'tags': ['tag1']}]} + expected = {'response': raw, 'status': 'threat', 'summary': 'abcd1234'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_caution(self): + # raw is empty + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'Triage': {'score': '6'}}, 'signature': 'abcd1234', 'tags': ['tag1']}]} + expected = {'response': raw, 'status': 'caution', 'summary': 'abcd1234'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_info(self): + # raw is empty + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'Triage': {'score': '3'}}, 'signature': 'abcd1234', 'tags': ['tag1']}]} + expected = {'response': raw, 'status': 'info', 'summary': 'abcd1234'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_ok(self): + # raw is empty + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'Triage': {'score': '1'}}, 'signature': 'abcd1234', 'tags': ['tag1']}]} + expected = {'response': raw, 'status': 'ok', 'summary': 'abcd1234'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_ok_tags(self): + # raw is empty + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'Triage': {'score': '1'}}, 'tags': ['tag1']}]} + expected = {'response': raw, 'status': 'ok', 'summary': 'tag1'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + + def test_prepareResults_ok_yomi(self): + # raw is empty + raw = {'query_status': 'ok', 'data': [{'sha256_hash': 'validhash', 'vendor_intel': {'YOROI_YOMI': {'detection': 'detection1', 'summary': '0.1'}}}]} + expected = {'response': raw, 'status': 'ok', 'summary': 'detection1'} + results = malwarebazaar.prepareResults(raw) + self.assertEqual(results, expected) + def test_buildReqGimqhash(self): result = malwarebazaar.buildReq('gimphash', '') self.assertEqual( @@ -67,3 +139,11 @@ class TestMalwarebazaarMethods(unittest.TestCase): result = malwarebazaar.buildReq('tlsh', '') self.assertEqual( result, {'query': 'get_tlsh', 'tlsh': ''}) + + # simulate API response and makes sure sendReq gives a response, we are just checking if sendReq gives back anything + def test_sendReq(self): + with patch('requests.post', new=MagicMock(return_value=MagicMock())) as mock: + response = malwarebazaar.sendReq( + {'baseUrl': 'https://www.randurl.xyz'}, 'example_data') + self.assertIsNotNone(response) + mock.assert_called_once()