From 1786b23b27c05d259ce3372cc0b9882651dbbb99 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 15 Oct 2019 16:04:03 +0200 Subject: [PATCH 1/9] add: Tests for expansion modules with different input types --- tests/test_expansions.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 6cfe953..3776a19 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -18,6 +18,13 @@ class TestExpansions(unittest.TestCase): def misp_modules_post(self, query): return requests.post(urljoin(self.url, "query"), json=query) + def get_data(self, response): + data = response.json() + if not isinstance(data, dict): + print(json.dumps(data, indent=2)) + return data + return data['results'][0]['data'] + def get_errors(self, response): data = response.json() if not isinstance(data, dict): @@ -103,6 +110,16 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'Samsung Electronics Co.,Ltd') + def test_otx(self): + query_types = ('domain', 'ip-src', 'md5') + query_values = ('circl.lu', '8.8.8.8', '616eff3e9a7575ae73821b4668d2801c') + results = ('149.13.33.14', 'ffc2595aefa80b61621023252b5f0ccb22b6e31d7f1640913cd8ff74ddbd8b41', + '8.8.8.8') + for query_type, query_value, result in zip(query_types, query_values, results): + query = {"module": "otx", query_type: query_value, "config": {"apikey": "1"}} + response = self.misp_modules_post(query) + self.assertTrue(self.get_values(response), [result]) + def test_rbl(self): query = {"module": "rbl", "ip-src": "8.8.8.8"} response = self.misp_modules_post(query) @@ -126,11 +143,27 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertTrue(self.get_values(response).startswith('Syntax valid:')) + def test_sourcecache(self): + input_value = "https://www.misp-project.org/feeds/" + query = {"module": "sourcecache", "link": input_value} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), input_value) + self.assertTrue(self.get_data(response).startswith('PCFET0NUWVBFIEhUTUw+CjwhLS0KCUFyY2FuYSBieSBIVE1MN')) + def test_stix2_pattern_validator(self): query = {"module": "stix2_pattern_syntax_validator", "stix2-pattern": "[ipv4-addr:value = '8.8.8.8']"} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'Syntax valid') + def test_threatcrowd(self): + query_types = ('domain', 'ip-src', 'md5', 'whois-registrant-email') + query_values = ('circl.lu', '149.13.33.4', '616eff3e9a7575ae73821b4668d2801c', 'hostmaster@eurodns.com') + results = ('149.13.33.14', 'cve.circl.lu', 'devilreturns.com', 'navabi.lu') + for query_type, query_value, result in zip(query_types, query_values, results): + query = {"module": "threatcrowd", query_type: query_value} + response = self.misp_modules_post(query) + self.assertTrue(self.get_values(response), [result]) + def test_wikidata(self): query = {"module": "wiki", "text": "Google"} response = self.misp_modules_post(query) From a7e523ab6135081743c0dd2f8fb58e59ffcb53ae Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 16 Oct 2019 22:00:36 +0200 Subject: [PATCH 2/9] add: threatminer module test --- tests/test_expansions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 3776a19..50249ef 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -164,6 +164,15 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertTrue(self.get_values(response), [result]) + def test_threatminer(self): + query_types = ('domain', 'ip-src', 'md5') + query_values = ('circl.lu', '149.13.33.4', 'b538dbc6160ef54f755a540e06dc27cd980fc4a12005e90b3627febb44a1a90f') + results = ('149.13.33.14', 'f6ecb9d5c21defb1f622364a30cb8274f817a1a2', 'http://www.circl.lu') + for query_type, query_value, result in zip(query_types, query_values, results): + query = {"module": "threatminer", query_type: query_value} + response = self.misp_modules_post(query) + self.assertTrue(self.get_values(response), [result]) + def test_wikidata(self): query = {"module": "wiki", "text": "Google"} response = self.misp_modules_post(query) From 9f7f11107c379d9d6302cfb099f0957b28e09b7a Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 17 Oct 2019 10:41:11 +0200 Subject: [PATCH 3/9] fix: Fixed ThreatMiner results parsing --- tests/test_expansions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 50249ef..17ca66c 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -167,11 +167,11 @@ class TestExpansions(unittest.TestCase): def test_threatminer(self): query_types = ('domain', 'ip-src', 'md5') query_values = ('circl.lu', '149.13.33.4', 'b538dbc6160ef54f755a540e06dc27cd980fc4a12005e90b3627febb44a1a90f') - results = ('149.13.33.14', 'f6ecb9d5c21defb1f622364a30cb8274f817a1a2', 'http://www.circl.lu') + results = ('149.13.33.14', 'f6ecb9d5c21defb1f622364a30cb8274f817a1a2', 'http://www.circl.lu/') for query_type, query_value, result in zip(query_types, query_values, results): query = {"module": "threatminer", query_type: query_value} response = self.misp_modules_post(query) - self.assertTrue(self.get_values(response), [result]) + self.assertTrue(self.get_values(response)[0], result) def test_wikidata(self): query = {"module": "wiki", "text": "Google"} From 60ef1901e2adced05d76a763e5a77c922e0ff66b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 17 Oct 2019 12:46:29 +0200 Subject: [PATCH 4/9] fix: Handling issues when the otx api is queried too often in a short time --- tests/test_expansions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 17ca66c..8350926 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -118,7 +118,11 @@ class TestExpansions(unittest.TestCase): for query_type, query_value, result in zip(query_types, query_values, results): query = {"module": "otx", query_type: query_value, "config": {"apikey": "1"}} response = self.misp_modules_post(query) - self.assertTrue(self.get_values(response), [result]) + try: + self.assertTrue(self.get_values(response), [result]) + except KeyError: + # Empty results, which in this case comes from a connection error + continue def test_rbl(self): query = {"module": "rbl", "ip-src": "8.8.8.8"} From 7aa78636a5aad022705432f58224a8bc2c24e6c8 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 17 Oct 2019 16:32:26 +0200 Subject: [PATCH 5/9] add: Tests for all the office, libreoffice, pdf & OCR enrich modules --- tests/test_expansions.py | 62 ++++++++++++++++++++++++++++++++- tests/test_files/misp-logo.png | Bin 0 -> 10376 bytes tests/test_files/test.ods | Bin 0 -> 7524 bytes tests/test_files/test.odt | Bin 0 -> 8181 bytes tests/test_files/test.pdf | Bin 0 -> 7169 bytes tests/test_files/test.pptx | Bin 0 -> 21025 bytes tests/test_files/test.xlsx | Bin 0 -> 5446 bytes 7 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/test_files/misp-logo.png create mode 100644 tests/test_files/test.ods create mode 100644 tests/test_files/test.odt create mode 100644 tests/test_files/test.pdf create mode 100644 tests/test_files/test.pptx create mode 100644 tests/test_files/test.xlsx diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 8350926..3afc51b 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -4,7 +4,9 @@ import unittest import requests from urllib.parse import urljoin +from base64 import b64encode import json +import os class TestExpansions(unittest.TestCase): @@ -85,6 +87,14 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), ['149.13.33.14']) + def test_docx(self): + filename = 'test.docx' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "docx-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), '\nThis is an basic test docx file. ') + def test_haveibeenpwned(self): query = {"module": "hibp", "email-src": "info@circl.lu"} response = self.misp_modules_post(query) @@ -96,7 +106,9 @@ class TestExpansions(unittest.TestCase): def test_greynoise(self): query = {"module": "greynoise", "ip-dst": "1.1.1.1"} response = self.misp_modules_post(query) - self.assertTrue(self.get_values(response).startswith('{"ip":"1.1.1.1","status":"ok"')) + value = self.get_values(response) + if value != 'GreyNoise API not accessible (HTTP 429)': + self.assertTrue(value.startswith('{"ip":"1.1.1.1","status":"ok"')) def test_ipasn(self): query = {"module": "ipasn", "ip-dst": "1.1.1.1"} @@ -110,6 +122,30 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'Samsung Electronics Co.,Ltd') + def test_ocr(self): + filename = 'misp-logo.png' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "ocr-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), 'Threat Sharing') + + def test_ods(self): + filename = 'test.ods' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "ods-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), '\n column_0\n0 ods test') + + def test_odt(self): + filename = 'test.odt' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "odt-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), 'odt test') + def test_otx(self): query_types = ('domain', 'ip-src', 'md5') query_values = ('circl.lu', '8.8.8.8', '616eff3e9a7575ae73821b4668d2801c') @@ -124,6 +160,22 @@ class TestExpansions(unittest.TestCase): # Empty results, which in this case comes from a connection error continue + def test_pdf(self): + filename = 'test.pdf' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "pdf-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), 'Pdf test') + + def test_pptx(self): + filename = 'test.pptx' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "pptx-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), '\npptx test\n') + def test_rbl(self): query = {"module": "rbl", "ip-src": "8.8.8.8"} response = self.misp_modules_post(query) @@ -187,6 +239,14 @@ class TestExpansions(unittest.TestCase): except Exception: self.assertEqual(self.get_values(response), 'No additional data found on Wikidata') + def test_xlsx(self): + filename = 'test.xlsx' + with open(f'tests/test_files/{filename}', 'rb') as f: + encoded = b64encode(f.read()).decode() + query = {"module": "xlsx-enrich", "attachment": filename, "data": encoded} + response = self.misp_modules_post(query) + self.assertEqual(self.get_values(response), ' header\n0 xlsx test') + def test_yara_query(self): query = {"module": "yara_query", "md5": "b2a5abfeef9e36964281a31e17b57c97"} response = self.misp_modules_post(query) diff --git a/tests/test_files/misp-logo.png b/tests/test_files/misp-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5f2d4dd577987fbaf4b5a0028cea64e69ae58eb0 GIT binary patch literal 10376 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 literal 0 HcmV?d00001 diff --git a/tests/test_files/test.ods b/tests/test_files/test.ods new file mode 100644 index 0000000000000000000000000000000000000000..080bb4a9762972012e98fffc96b434d687b2fc44 GIT binary patch literal 7524 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 literal 0 HcmV?d00001 diff --git a/tests/test_files/test.odt b/tests/test_files/test.odt new file mode 100644 index 0000000000000000000000000000000000000000..a554904dd7c9cf4a1238411efd27dac1cd9298cc GIT binary patch literal 8181 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 T+93h}#8*GamD6#fzP|e}$IFlG literal 0 HcmV?d00001 diff --git a/tests/test_files/test.pdf b/tests/test_files/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..79d960a0ba92a9f598ed38554164a500e33d7d90 GIT binary patch literal 7169 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 literal 0 HcmV?d00001 diff --git a/tests/test_files/test.pptx b/tests/test_files/test.pptx new file mode 100644 index 0000000000000000000000000000000000000000..c1223b23e2f50c5600e9b8c685dfd40dd4fc38b6 GIT binary patch literal 21025 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 literal 0 HcmV?d00001 diff --git a/tests/test_files/test.xlsx b/tests/test_files/test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..093daf25eede5b3586d89780a329912de02f54f3 GIT binary patch literal 5446 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 literal 0 HcmV?d00001 From 93bc178717f3a16b377e0ebe09eb084cbc010fc4 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 17 Oct 2019 16:36:56 +0200 Subject: [PATCH 6/9] fix: Removed unused import\ --- tests/test_expansions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 3afc51b..73cfcf9 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -6,7 +6,6 @@ import requests from urllib.parse import urljoin from base64 import b64encode import json -import os class TestExpansions(unittest.TestCase): From cf73151ebca6ac3317eadb710e701aebf8df4a7c Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Thu, 17 Oct 2019 16:58:27 +0200 Subject: [PATCH 7/9] fix: Fixed tesseract python library issues - Avoiding 'tesseract is not installed or it's not in your path' issues --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index db66efd..99b8af0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ before_install: - docker build -t misp-modules --build-arg BUILD_DATE=$(date -u +"%Y-%m-%d") docker/ install: - - sudo apt-get install libzbar0 libzbar-dev libpoppler-cpp-dev + - sudo apt-get install libzbar0 libzbar-dev libpoppler-cpp-dev tesseract-ocr - pip install pipenv - pipenv install --dev From 6df0072e60e746a60b2422c367e146166cff6ffb Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 18 Oct 2019 09:43:53 +0200 Subject: [PATCH 8/9] fix: Using absolute path to open files instead of relative path --- tests/test_expansions.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 73cfcf9..d2ab54c 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -6,6 +6,7 @@ import requests from urllib.parse import urljoin from base64 import b64encode import json +import os class TestExpansions(unittest.TestCase): @@ -14,6 +15,7 @@ class TestExpansions(unittest.TestCase): self.maxDiff = None self.headers = {'Content-Type': 'application/json'} self.url = "http://127.0.0.1:6666/" + self.dirname = os.path.dirname(os.path.realpath(__file__)) self.sigma_rule = "title: Antivirus Web Shell Detection\r\ndescription: Detects a highly relevant Antivirus alert that reports a web shell\r\ndate: 2018/09/09\r\nmodified: 2019/10/04\r\nauthor: Florian Roth\r\nreferences:\r\n - https://www.nextron-systems.com/2018/09/08/antivirus-event-analysis-cheat-sheet-v1-4/\r\ntags:\r\n - attack.persistence\r\n - attack.t1100\r\nlogsource:\r\n product: antivirus\r\ndetection:\r\n selection:\r\n Signature: \r\n - \"PHP/Backdoor*\"\r\n - \"JSP/Backdoor*\"\r\n - \"ASP/Backdoor*\"\r\n - \"Backdoor.PHP*\"\r\n - \"Backdoor.JSP*\"\r\n - \"Backdoor.ASP*\"\r\n - \"*Webshell*\"\r\n condition: selection\r\nfields:\r\n - FileName\r\n - User\r\nfalsepositives:\r\n - Unlikely\r\nlevel: critical" def misp_modules_post(self, query): @@ -88,7 +90,7 @@ class TestExpansions(unittest.TestCase): def test_docx(self): filename = 'test.docx' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "docx-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -123,7 +125,7 @@ class TestExpansions(unittest.TestCase): def test_ocr(self): filename = 'misp-logo.png' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "ocr-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -131,7 +133,7 @@ class TestExpansions(unittest.TestCase): def test_ods(self): filename = 'test.ods' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "ods-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -139,7 +141,7 @@ class TestExpansions(unittest.TestCase): def test_odt(self): filename = 'test.odt' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "odt-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -161,7 +163,7 @@ class TestExpansions(unittest.TestCase): def test_pdf(self): filename = 'test.pdf' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "pdf-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -169,7 +171,7 @@ class TestExpansions(unittest.TestCase): def test_pptx(self): filename = 'test.pptx' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "pptx-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) @@ -240,7 +242,7 @@ class TestExpansions(unittest.TestCase): def test_xlsx(self): filename = 'test.xlsx' - with open(f'tests/test_files/{filename}', 'rb') as f: + with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() query = {"module": "xlsx-enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) From 63dba29c52874c0ddafbc1a47b89705a7d982c80 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 18 Oct 2019 11:09:10 +0200 Subject: [PATCH 9/9] fix: Fixed module names with - to avoid errors with python paths --- misp_modules/modules/expansion/__init__.py | 4 ++-- .../expansion/{docx-enrich.py => docx_enrich.py} | 0 .../expansion/{ocr-enrich.py => ocr_enrich.py} | 0 .../expansion/{ods-enrich.py => ods_enrich.py} | 0 .../expansion/{odt-enrich.py => odt_enrich.py} | 0 .../expansion/{pdf-enrich.py => pdf_enrich.py} | 0 .../expansion/{pptx-enrich.py => pptx_enrich.py} | 0 .../expansion/{xlsx-enrich.py => xlsx_enrich.py} | 0 tests/test_expansions.py | 14 +++++++------- 9 files changed, 9 insertions(+), 9 deletions(-) rename misp_modules/modules/expansion/{docx-enrich.py => docx_enrich.py} (100%) rename misp_modules/modules/expansion/{ocr-enrich.py => ocr_enrich.py} (100%) rename misp_modules/modules/expansion/{ods-enrich.py => ods_enrich.py} (100%) rename misp_modules/modules/expansion/{odt-enrich.py => odt_enrich.py} (100%) rename misp_modules/modules/expansion/{pdf-enrich.py => pdf_enrich.py} (100%) rename misp_modules/modules/expansion/{pptx-enrich.py => pptx_enrich.py} (100%) rename misp_modules/modules/expansion/{xlsx-enrich.py => xlsx_enrich.py} (100%) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index ef31ad9..9fe3b5c 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -12,6 +12,6 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'xforceexchange', 'sigma_syntax_validator', 'stix2_pattern_syntax_validator', 'sigma_queries', 'dbl_spamhaus', 'vulners', 'yara_query', 'macaddress_io', 'intel471', 'backscatter_io', 'btc_scam_check', 'hibp', 'greynoise', 'macvendors', - 'qrcode', 'ocr-enrich', 'pdf-enrich', 'docx-enrich', 'xlsx-enrich', 'pptx-enrich', - 'ods-enrich', 'odt-enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', + 'qrcode', 'ocr_enrich', 'pdf_enrich', 'docx_enrich', 'xlsx_enrich', 'pptx_enrich', + 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public'] diff --git a/misp_modules/modules/expansion/docx-enrich.py b/misp_modules/modules/expansion/docx_enrich.py similarity index 100% rename from misp_modules/modules/expansion/docx-enrich.py rename to misp_modules/modules/expansion/docx_enrich.py diff --git a/misp_modules/modules/expansion/ocr-enrich.py b/misp_modules/modules/expansion/ocr_enrich.py similarity index 100% rename from misp_modules/modules/expansion/ocr-enrich.py rename to misp_modules/modules/expansion/ocr_enrich.py diff --git a/misp_modules/modules/expansion/ods-enrich.py b/misp_modules/modules/expansion/ods_enrich.py similarity index 100% rename from misp_modules/modules/expansion/ods-enrich.py rename to misp_modules/modules/expansion/ods_enrich.py diff --git a/misp_modules/modules/expansion/odt-enrich.py b/misp_modules/modules/expansion/odt_enrich.py similarity index 100% rename from misp_modules/modules/expansion/odt-enrich.py rename to misp_modules/modules/expansion/odt_enrich.py diff --git a/misp_modules/modules/expansion/pdf-enrich.py b/misp_modules/modules/expansion/pdf_enrich.py similarity index 100% rename from misp_modules/modules/expansion/pdf-enrich.py rename to misp_modules/modules/expansion/pdf_enrich.py diff --git a/misp_modules/modules/expansion/pptx-enrich.py b/misp_modules/modules/expansion/pptx_enrich.py similarity index 100% rename from misp_modules/modules/expansion/pptx-enrich.py rename to misp_modules/modules/expansion/pptx_enrich.py diff --git a/misp_modules/modules/expansion/xlsx-enrich.py b/misp_modules/modules/expansion/xlsx_enrich.py similarity index 100% rename from misp_modules/modules/expansion/xlsx-enrich.py rename to misp_modules/modules/expansion/xlsx_enrich.py diff --git a/tests/test_expansions.py b/tests/test_expansions.py index d2ab54c..a3a2c8e 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -92,7 +92,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.docx' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "docx-enrich", "attachment": filename, "data": encoded} + query = {"module": "docx_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), '\nThis is an basic test docx file. ') @@ -127,7 +127,7 @@ class TestExpansions(unittest.TestCase): filename = 'misp-logo.png' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "ocr-enrich", "attachment": filename, "data": encoded} + query = {"module": "ocr_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'Threat Sharing') @@ -135,7 +135,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.ods' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "ods-enrich", "attachment": filename, "data": encoded} + query = {"module": "ods_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), '\n column_0\n0 ods test') @@ -143,7 +143,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.odt' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "odt-enrich", "attachment": filename, "data": encoded} + query = {"module": "odt_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'odt test') @@ -165,7 +165,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.pdf' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "pdf-enrich", "attachment": filename, "data": encoded} + query = {"module": "pdf_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), 'Pdf test') @@ -173,7 +173,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.pptx' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "pptx-enrich", "attachment": filename, "data": encoded} + query = {"module": "pptx_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), '\npptx test\n') @@ -244,7 +244,7 @@ class TestExpansions(unittest.TestCase): filename = 'test.xlsx' with open(f'{self.dirname}/test_files/{filename}', 'rb') as f: encoded = b64encode(f.read()).decode() - query = {"module": "xlsx-enrich", "attachment": filename, "data": encoded} + query = {"module": "xlsx_enrich", "attachment": filename, "data": encoded} response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), ' header\n0 xlsx test')