From 0e6d514198213f68d22262a67e6057e25359b9b6 Mon Sep 17 00:00:00 2001 From: StefanKelm Date: Wed, 16 Oct 2019 12:40:22 +0200 Subject: [PATCH 001/220] Update test_expansions.py Tiniest of typos --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 6cfe953..1899902 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -45,7 +45,7 @@ class TestExpansions(unittest.TestCase): def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} response = self.misp_modules_post(query) - self.assertEqual(self.get_values(response), '1es14c7qlb5cyhlmuekctxlgc1fv2ti9da fraudolent bitcoin address') + self.assertEqual(self.get_values(response), '1es14c7qlb5cyhlmuekctxlgc1fv2ti9da fraudulent bitcoin address') def test_countrycode(self): query = {"module": "countrycode", "domain": "www.circl.lu"} From c4d333f8b9c5c9d34cf2ae93682b8c9456f1f25c Mon Sep 17 00:00:00 2001 From: Braden Laverick Date: Thu, 31 Oct 2019 17:20:35 +0000 Subject: [PATCH 002/220] Updated README to include EQL modules --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 462e4c1..5cade1d 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [docx-enrich](misp_modules/modules/expansion/docx-enrich.py) - an enrichment module to get text out of Word document into MISP (using free-text parser). * [DomainTools](misp_modules/modules/expansion/domaintools.py) - a hover and expansion module to get information from [DomainTools](http://www.domaintools.com/) whois. * [EUPI](misp_modules/modules/expansion/eupi.py) - a hover and expansion module to get information about an URL from the [Phishing Initiative project](https://phishing-initiative.eu/?lang=en). +* [EQL](misp_modules/modules/expansion/eql.py) - an expansion module to generate event query language (EQL) from an attribute. [Event Query Language](https://eql.readthedocs.io/en/latest/) * [Farsight DNSDB Passive DNS](misp_modules/modules/expansion/farsight_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. * [GeoIP](misp_modules/modules/expansion/geoip_country.py) - a hover and expansion module to get GeoIP information from geolite/maxmind. * [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. @@ -86,6 +87,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [GoAML export](misp_modules/modules/export_mod/goamlexport.py) module to export in [GoAML format](http://goaml.unodc.org/goaml/en/index.html). * [Lite Export](misp_modules/modules/export_mod/liteexport.py) module to export a lite event. * [PDF export](misp_modules/modules/export_mod/pdfexport.py) module to export an event in PDF. +* [Mass EQL Export](misp_modules/modules/export_mod/mass_eql_export.py) module to export applicable attributes from an event to a mass EQL query. * [Nexthink query format](misp_modules/modules/export_mod/nexthinkexport.py) module to export in Nexthink query format. * [osquery](misp_modules/modules/export_mod/osqueryexport.py) module to export in [osquery](https://osquery.io/) query format. * [ThreatConnect](misp_modules/modules/export_mod/threat_connect_export.py) module to export in ThreatConnect CSV format. From 26ab7f69e23c4fe707529d95267e6c4c5e963f17 Mon Sep 17 00:00:00 2001 From: Braden Laverick Date: Thu, 31 Oct 2019 17:28:07 +0000 Subject: [PATCH 003/220] Added documentation json for new modules --- doc/expansion/eql.json | 8 ++++++++ doc/export_mod/mass_eql_export.json | 8 ++++++++ docs/index.md | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 doc/expansion/eql.json create mode 100644 doc/export_mod/mass_eql_export.json diff --git a/doc/expansion/eql.json b/doc/expansion/eql.json new file mode 100644 index 0000000..bc5e71f --- /dev/null +++ b/doc/expansion/eql.json @@ -0,0 +1,8 @@ +{ + "description": "EQL query generation for a MISP attribute.", + "requirements": [], + "features": "This module adds a new attribute to a MISP event containing an EQL query for a network or file attribute.", + "references": [], + "input": "MISP Event attributes", + "output": "Event attribute containing EQL for a network or file attribute." + } \ No newline at end of file diff --git a/doc/export_mod/mass_eql_export.json b/doc/export_mod/mass_eql_export.json new file mode 100644 index 0000000..ae18938 --- /dev/null +++ b/doc/export_mod/mass_eql_export.json @@ -0,0 +1,8 @@ +{ + "description": "Mass EQL query export for a MISP event.", + "requirements": [], + "features": "This module produces EQL queries for all relevant attributes in a MISP event.", + "references": [], + "input": "MISP Event attributes", + "output": "Text file containing one or more EQL queries" + } \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index bb09e5a..1297a3b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,6 +35,7 @@ For more information: [Extending MISP with Python modules](https://www.circl.lu/ * [docx-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/docx-enrich.py) - an enrichment module to get text out of Word document into MISP (using free-text parser). * [DomainTools](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/domaintools.py) - a hover and expansion module to get information from [DomainTools](http://www.domaintools.com/) whois. * [EUPI](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/eupi.py) - a hover and expansion module to get information about an URL from the [Phishing Initiative project](https://phishing-initiative.eu/?lang=en). +* [EQL](misp_modules/modules/expansion/eql.py) - an expansion module to generate event query language (EQL) from an attribute. [Event Query Language](https://eql.readthedocs.io/en/latest/) * [Farsight DNSDB Passive DNS](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/farsight_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. * [GeoIP](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_country.py) - a hover and expansion module to get GeoIP information from geolite/maxmind. * [Greynoise](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. @@ -87,6 +88,7 @@ For more information: [Extending MISP with Python modules](https://www.circl.lu/ * [Cisco FireSight Manager ACL rule](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py) module to export as rule for the Cisco FireSight manager ACL. * [GoAML export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/goamlexport.py) module to export in [GoAML format](http://goaml.unodc.org/goaml/en/index.html). * [Lite Export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/liteexport.py) module to export a lite event. +* [Mass EQL Export](misp_modules/modules/export_mod/mass_eql_export.py) module to export applicable attributes from an event to a mass EQL query. * [PDF export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/pdfexport.py) module to export an event in PDF. * [Nexthink query format](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/nexthinkexport.py) module to export in Nexthink query format. * [osquery](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/osqueryexport.py) module to export in [osquery](https://osquery.io/) query format. From 204f59de137bd85cf2c9e833743a75db04083f27 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 7 Nov 2019 09:54:32 +0100 Subject: [PATCH 004/220] add: Updated documentation with the EQL export module --- doc/README.md | 22 +++++++++++++++++++--- doc/export_mod/mass_eql_export.json | 5 +++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/doc/README.md b/doc/README.md index 54100c0..7cf7a7c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -330,13 +330,13 @@ DomainTools MISP expansion module. -Generates EQL queries from attributes +EQL query generation for a MISP attribute. - **features**: ->The module simply generates EQL rules out of the input attribute. +>This module adds a new attribute to a MISP event containing an EQL query for a network or file attribute. - **input**: >A filename or ip attribute. - **output**: ->The EQL query generated from the input attribute. +>Attribute containing EQL for a network or file attribute. - **references**: >https://eql.readthedocs.io/en/latest/ @@ -1378,6 +1378,22 @@ Lite export of a MISP event. ----- +#### [mass_eql_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/mass_eql_export.py) + + + +Mass EQL query export for a MISP event. +- **features**: +>This module produces EQL queries for all relevant attributes in a MISP event. +- **input**: +>MISP Event attributes +- **output**: +>Text file containing one or more EQL queries +- **references**: +>https://eql.readthedocs.io/en/latest/ + +----- + #### [nexthinkexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/nexthinkexport.py) diff --git a/doc/export_mod/mass_eql_export.json b/doc/export_mod/mass_eql_export.json index ae18938..5eadd23 100644 --- a/doc/export_mod/mass_eql_export.json +++ b/doc/export_mod/mass_eql_export.json @@ -1,8 +1,9 @@ { "description": "Mass EQL query export for a MISP event.", + "logo": "logos/eql.png", "requirements": [], "features": "This module produces EQL queries for all relevant attributes in a MISP event.", - "references": [], + "references": ["https://eql.readthedocs.io/en/latest/"], "input": "MISP Event attributes", "output": "Text file containing one or more EQL queries" - } \ No newline at end of file + } From 474307ac5b7d5639eeeb65403a3d347ec569c3c3 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 7 Nov 2019 09:57:18 +0100 Subject: [PATCH 005/220] chg: Using EQL module description from blaverick62 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 44142c7..1bc8d1f 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,8 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [DNS](misp_modules/modules/expansion/dns.py) - a simple module to resolve MISP attributes like hostname and domain to expand IP addresses attributes. * [docx-enrich](misp_modules/modules/expansion/docx_enrich.py) - an enrichment module to get text out of Word document into MISP (using free-text parser). * [DomainTools](misp_modules/modules/expansion/domaintools.py) - a hover and expansion module to get information from [DomainTools](http://www.domaintools.com/) whois. -* [EQL](misp_modules/modules/expansion/eql.py) - an expansion module to generate EQL queries from attributes. -* [EUPI](misp_modules/modules/expansion/eupi.py) - a hover and expansion module to get information about an URL from the [Phishing Initiative project](https://phishing-initiative.eu/?lang=en). * [EQL](misp_modules/modules/expansion/eql.py) - an expansion module to generate event query language (EQL) from an attribute. [Event Query Language](https://eql.readthedocs.io/en/latest/) +* [EUPI](misp_modules/modules/expansion/eupi.py) - a hover and expansion module to get information about an URL from the [Phishing Initiative project](https://phishing-initiative.eu/?lang=en). * [Farsight DNSDB Passive DNS](misp_modules/modules/expansion/farsight_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. * [GeoIP](misp_modules/modules/expansion/geoip_country.py) - a hover and expansion module to get GeoIP information from geolite/maxmind. * [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. From 91d6f1baa0b9d6931ead5d846686843beb9c7d68 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 7 Nov 2019 11:50:16 +0100 Subject: [PATCH 006/220] fix: Fixed csv file parsing --- misp_modules/modules/import_mod/csvimport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index d5e2d59..96e42b1 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -206,11 +206,11 @@ def __any_mandatory_misp_field(header): def __special_parsing(data, delimiter): - return list(line.split(delimiter) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line.startswith('#')) + return list(tuple(l.strip() for l in line[0].split(delimiter)) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) def __standard_parsing(data): - return list(line for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) + return list(tuple(l.strip() for l in line) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) def handler(q=False): From 4990bcebd8685412362c2266fa724a0ac3b46a95 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sun, 17 Nov 2019 18:00:19 -0500 Subject: [PATCH 007/220] fix: Avoiding KeyError exception when no result is found --- misp_modules/modules/expansion/xforceexchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/xforceexchange.py b/misp_modules/modules/expansion/xforceexchange.py index 63af8db..7999ce2 100644 --- a/misp_modules/modules/expansion/xforceexchange.py +++ b/misp_modules/modules/expansion/xforceexchange.py @@ -105,7 +105,7 @@ class XforceExchange(): def _parse_dns(self, value): dns_result = self._api_call(f'{self.base_url}/resolve/{value}') - if dns_result and dns_result['Passive'].get('records'): + if dns_result.get('Passive') and dns_result['Passive'].get('records'): itype, ftype, value = self._fetch_types(dns_result['Passive']['query']) misp_object = MISPObject('domain-ip') misp_object.add_attribute(itype, value) From f08fc6d9a5784da92200e0344ab4f75398af4b14 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sun, 17 Nov 2019 19:11:26 -0500 Subject: [PATCH 008/220] chg: Reintroducing the limit to reduce the number of recursive calls to the API when querying for a domain --- misp_modules/modules/expansion/virustotal.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index cd0e738..77a99a2 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -12,14 +12,13 @@ moduleinfo = {'version': '4', 'author': 'Hannah Ward', 'module-type': ['expansion']} # config fields that your code expects from the site admin -moduleconfig = ["apikey"] +moduleconfig = ["apikey", "event_limit"] -# TODO: Parse the report with a private API key to be able to get more advanced results from a query with 'allinfo' set to True - class VirusTotalParser(object): - def __init__(self, apikey): + def __init__(self, apikey, limit): self.apikey = apikey + self.limit = limit self.base_url = "https://www.virustotal.com/vtapi/v2/{}/report" self.misp_event = MISPEvent() self.parsed_objects = {} @@ -57,7 +56,7 @@ class VirusTotalParser(object): uuid = self.parse_resolutions(req['resolutions'], req['subdomains'], siblings) for feature_type, relationship in feature_types.items(): for feature in ('undetected_{}_samples', 'detected_{}_samples'): - for sample in req.get(feature.format(feature_type), []): + for sample in req.get(feature.format(feature_type), [])[:self.limit]: status_code = self.parse_hash(sample[hash_type], False, uuid, relationship) if status_code != 200: return status_code @@ -197,7 +196,10 @@ def handler(q=False): if not request.get('config') or not request['config'].get('apikey'): misperrors['error'] = "A VirusTotal api key is required for this module." return misperrors - parser = VirusTotalParser(request['config']['apikey']) + event_limit = request['config'].get('event_limit') + if not isinstance(event_limit, int): + event_limit = 5 + parser = VirusTotalParser(request['config']['apikey'], event_limit) attribute = request['attribute'] status = parser.query_api(attribute) if status != 200: From 58a4cb15a1b091537b1dbad3a9ef056062c1c268 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 19 Nov 2019 15:41:35 -0500 Subject: [PATCH 009/220] add: New expansion module to submit samples and urls to AssemblyLine --- misp_modules/modules/expansion/__init__.py | 3 +- .../modules/expansion/assemblyline_submit.py | 87 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/assemblyline_submit.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 9a1f309..04c43e6 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -14,4 +14,5 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c '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', - 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails'] + 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', + 'assemblyline_submit'] diff --git a/misp_modules/modules/expansion/assemblyline_submit.py b/misp_modules/modules/expansion/assemblyline_submit.py new file mode 100644 index 0000000..19f5f3c --- /dev/null +++ b/misp_modules/modules/expansion/assemblyline_submit.py @@ -0,0 +1,87 @@ +import json + +from assemblyline_client import Client, ClientError +from urllib.parse import urljoin + + +moduleinfo = {"version": 1, "author": "Christian Studer", "module-type": ["expansion"], + "description": "Submit files or URLs to AssemblyLine"} +moduleconfig = ["apiurl", "user_id", "apikey", "password"] +mispattributes = {"input": ["attachment", "malware-sample", "url", "domain"], + "output": ["link"]} + + +def parse_config(apiurl, user_id, config): + error = {"error": "Please provide your AssemblyLine API key or Password."} + if config.get('apikey'): + try: + return Client(apiurl, apikey=(user_id, config['apikey'])) + except ClientError as e: + error['error'] = f'Error while initiating a connection with AssemblyLine: {e.__str__()}' + if config.get('password'): + try: + return Client(apiurl, auth=(user_id, config['password'])) + except ClientError as e: + error['error'] = f'Error while initiating a connection with AssemblyLine: {e.__str__()}' + return error + +def submit_content(client, filename, data): + try: + return client.submit(fname=filename, contents=data.encode()) + except Exception as e: + return {'error': f'Error while submitting content to AssemblyLine: {e.__str__()}'} + + +def submit_request(client, request): + if 'attachment' in request: + return submit_content(client, request['attachment'], request['data']) + if 'malware-sample' in request: + return submit_content(client, request['malware-sample'].split('|')[0], request['data']) + for feature in ('url', 'domain'): + if feature in request: + return submit_url(client, request[feature]) + return {"error": "No valid attribute type for this module has been provided."} + + +def submit_url(client, url): + try: + return client.submit(url=url) + except Exception as e: + return {'error': f'Error while submitting url to AssemblyLine: {e.__str__()}'} + + +def handler(q=False): + if q is False: + return q + request = json.loads(q) + if not request.get('config'): + return {"error": "Missing configuration."} + if not request['config'].get('apiurl'): + return {"error": "No AssemblyLine server address provided."} + apiurl = request['config']['apiurl'] + if not request['config'].get('user_id'): + return {"error": "Please provide your AssemblyLine User ID."} + user_id = request['config']['user_id'] + client = parse_config(apiurl, user_id, request['config']) + if isinstance(client, dict): + return client + submission = submit_request(client, request) + if 'error' in submission: + return submission + sid = submission['submission']['sid'] + return { + "results": [{ + "types": "link", + "categories": "External analysis", + "values": urljoin(apiurl, f'submission_detail.html?sid={sid}') + }] + } + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo From fb129106ab74e25a8a7cf603cb71d74b82da01c6 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 19 Nov 2019 16:05:16 -0500 Subject: [PATCH 010/220] add: Updated python dependencies to include the assemblyline_client library --- Pipfile | 1 + Pipfile.lock | 422 +++++++++++++++++++++++++++++++++++---------------- REQUIREMENTS | 1 + 3 files changed, 294 insertions(+), 130 deletions(-) diff --git a/Pipfile b/Pipfile index bce4c5b..415178b 100644 --- a/Pipfile +++ b/Pipfile @@ -58,6 +58,7 @@ idna-ssl = {markers = "python_version < '3.7'"} jbxapi = "*" geoip2 = "*" apiosintDS = "*" +assemblyline_client = "*" [requires] python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index 37f5272..8d6be41 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e31638147f27ca5c90e27ebecdeb871f027feb37ede229b4296da35094a9516f" + "sha256": "28bab177e7e34c6b7fe8bfd8be6fe79a87ec6ca9c44ca63148fed9433d09cf21" }, "pipfile-spec": 6, "requires": { @@ -52,10 +52,10 @@ }, "apiosintds": { "hashes": [ - "sha256:9a92f3fdb265f49046a871338419709f784b8ed82b249435c3c40e47d2ab4bcf" + "sha256:d8ab4dcf75a9989572cd6808773b56fdf535b6080d6041d98e911e6c5eb31f3c" ], "index": "pypi", - "version": "==1.8.2" + "version": "==1.8.3" }, "argparse": { "hashes": [ @@ -64,6 +64,14 @@ ], "version": "==1.4.0" }, + "assemblyline-client": { + "hashes": [ + "sha256:6f45cab3be3ec60984a5c2049d46dac80d4e3d4f3d9538220518a44c7a6ddb15", + "sha256:971371065f2b41027325bf9fa9c72960262a446c7e08bda57865d34dcc4108b0" + ], + "index": "pypi", + "version": "==3.7.3" + }, "async-timeout": { "hashes": [ "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", @@ -109,6 +117,44 @@ ], "version": "==2019.9.11" }, + "cffi": { + "hashes": [ + "sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42", + "sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04", + "sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5", + "sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54", + "sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba", + "sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57", + "sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396", + "sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12", + "sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97", + "sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43", + "sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db", + "sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3", + "sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b", + "sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579", + "sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346", + "sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159", + "sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652", + "sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e", + "sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a", + "sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506", + "sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f", + "sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d", + "sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c", + "sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20", + "sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858", + "sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc", + "sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a", + "sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3", + "sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e", + "sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410", + "sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25", + "sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b", + "sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d" + ], + "version": "==1.13.2" + }, "chardet": { "hashes": [ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", @@ -137,6 +183,32 @@ ], "version": "==0.4.1" }, + "cryptography": { + "hashes": [ + "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c", + "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595", + "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad", + "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651", + "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2", + "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff", + "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d", + "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42", + "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d", + "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e", + "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912", + "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793", + "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13", + "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7", + "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0", + "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879", + "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f", + "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9", + "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2", + "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf", + "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8" + ], + "version": "==2.8" + }, "decorator": { "hashes": [ "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce", @@ -146,10 +218,10 @@ }, "deprecated": { "hashes": [ - "sha256:a515c4cf75061552e0284d123c3066fbbe398952c87333a92b8fc3dd8e4f9cc1", - "sha256:b07b414c8aac88f60c1d837d21def7e83ba711052e03b3cbaff27972567a8f8d" + "sha256:408038ab5fdeca67554e8f6742d1521cd3cd0ee0ff9d47f29318a4f4da31c308", + "sha256:8b6a5aa50e482d8244a62e5582b96c372e87e3a28e8b49c316e46b95c76a611d" ], - "version": "==1.2.6" + "version": "==1.2.7" }, "dnspython": { "hashes": [ @@ -227,6 +299,7 @@ "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" ], + "markers": "python_version < '3.8'", "version": "==0.23" }, "isodate": { @@ -245,10 +318,10 @@ }, "jsonschema": { "hashes": [ - "sha256:2fa0684276b6333ff3c0b1b27081f4b2305f0a36cf702a23db50edb141893c3f", - "sha256:94c0a13b4a0616458b42529091624e66700a17f847453e52279e35509a5b7631" + "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163", + "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a" ], - "version": "==3.1.1" + "version": "==3.2.0" }, "lxml": { "hashes": [ @@ -350,29 +423,29 @@ }, "numpy": { "hashes": [ - "sha256:0b0dd8f47fb177d00fa6ef2d58783c4f41ad3126b139c91dd2f7c4b3fdf5e9a5", - "sha256:25ffe71f96878e1da7e014467e19e7db90ae7d4e12affbc73101bcf61785214e", - "sha256:26efd7f7d755e6ca966a5c0ac5a930a87dbbaab1c51716ac26a38f42ecc9bc4b", - "sha256:28b1180c758abf34a5c3fea76fcee66a87def1656724c42bb14a6f9717a5bdf7", - "sha256:2e418f0a59473dac424f888dd57e85f77502a593b207809211c76e5396ae4f5c", - "sha256:30c84e3a62cfcb9e3066f25226e131451312a044f1fe2040e69ce792cb7de418", - "sha256:4650d94bb9c947151737ee022b934b7d9a845a7c76e476f3e460f09a0c8c6f39", - "sha256:4dd830a11e8724c9c9379feed1d1be43113f8bcce55f47ea7186d3946769ce26", - "sha256:4f2a2b279efde194877aff1f76cf61c68e840db242a5c7169f1ff0fd59a2b1e2", - "sha256:62d22566b3e3428dfc9ec972014c38ed9a4db4f8969c78f5414012ccd80a149e", - "sha256:669795516d62f38845c7033679c648903200980d68935baaa17ac5c7ae03ae0c", - "sha256:75fcd60d682db3e1f8fbe2b8b0c6761937ad56d01c1dc73edf4ef2748d5b6bc4", - "sha256:9395b0a41e8b7e9a284e3be7060db9d14ad80273841c952c83a5afc241d2bd98", - "sha256:9e37c35fc4e9410093b04a77d11a34c64bf658565e30df7cbe882056088a91c1", - "sha256:a0678793096205a4d784bd99f32803ba8100f639cf3b932dc63b21621390ea7e", - "sha256:b46554ad4dafb2927f88de5a1d207398c5385edbb5c84d30b3ef187c4a3894d8", - "sha256:c867eeccd934920a800f65c6068acdd6b87e80d45cd8c8beefff783b23cdc462", - "sha256:dd0667f5be56fb1b570154c2c0516a528e02d50da121bbbb2cbb0b6f87f59bc2", - "sha256:de2b1c20494bdf47f0160bd88ed05f5e48ae5dc336b8de7cfade71abcc95c0b9", - "sha256:f1df7b2b7740dd777571c732f98adb5aad5450aee32772f1b39249c8a50386f6", - "sha256:ffca69e29079f7880c5392bf675eb8b4146479d976ae1924d01cd92b04cccbcc" + "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", + "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", + "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", + "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", + "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", + "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", + "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", + "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", + "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", + "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", + "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", + "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", + "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", + "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", + "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", + "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", + "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", + "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", + "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", + "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", + "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" ], - "version": "==1.17.3" + "version": "==1.17.4" }, "oauth2": { "hashes": [ @@ -422,28 +495,28 @@ }, "pandas": { "hashes": [ - "sha256:0f484f43658a72e7d586a74978259657839b5bd31b903e963bb1b1491ab51775", - "sha256:0ffc6f9e20e77f3a7dc8baaad9c7fd25b858b084d3a2d8ce877bc3ea804e0636", - "sha256:23e0eac646419c3057f15eb96ab821964848607bf1d4ea5a82f26565986ec5e9", - "sha256:27c0603b15b5c6fa24885253bbe49a0c289381e7759385c59308ba4f0b166cf1", - "sha256:397fe360643fffc5b26b41efdf608647e3334a618d185a07976cd2dc51c90bce", - "sha256:3dbb3aa41c01504255bff2bd56944bdb915f6c9ce4bac7e2868efbace0b2a639", - "sha256:4e07c63247c59d61c6ebdbbb50196143cec6c5044403510c4e1a9d31854a83d6", - "sha256:4fa6d9235c6d2fecbeca82c3d326abd255866cafbfd37f66a0e826544e619760", - "sha256:56cb88b3876363d410a9d7724f43641ff164e2c9902d3266a648213e2efd5e6d", - "sha256:7ce1be1614455f83710b9a5dc1fc602a755bdddbe4dda1d41515062923a37bbf", - "sha256:ae1c96ffdeec376895e533107e0b0f9da16225a2184fbb24a5abc866769db75e", - "sha256:b6f27c9231be8a23de846f2302373991467dd8e397a4804d2614e8c5aa8d5a90", - "sha256:c6056067f894f9355bedcd168dd740aa849908d41c0a74756f6e38f203e941b3", - "sha256:ca91a19d1f0a280874a24dca44aadce42da7f3a7edb7e9ab7c7baad8febee2be", - "sha256:cbe4985f5c82a173f8cff6b7fe92d551addf99fb4ea9ff4eb4b1fe606bb098ec", - "sha256:e3e9893bfe80a8b8e6d56d36ebb7afe1df77db7b4068a6e2ef3636a91f6f1caa", - "sha256:e7b218e8711910dac3fed0d19376cd1ef0e386be5175965d332fd0c65d02a43b", - "sha256:ec48d18b8b63a5dbb838e8ea7892ee1034299e03f852bd9b6dffe870310414dd", - "sha256:f4ab6280277e3208a59bfa9f2e51240304d09e69ffb65abfb4a21d678b495f74" + "sha256:00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d", + "sha256:22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e", + "sha256:255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b", + "sha256:26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7", + "sha256:33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2", + "sha256:4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9", + "sha256:52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4", + "sha256:61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0", + "sha256:6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71", + "sha256:7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3", + "sha256:78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b", + "sha256:8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f", + "sha256:975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17", + "sha256:9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d", + "sha256:adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a", + "sha256:bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf", + "sha256:df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133", + "sha256:e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7", + "sha256:ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c" ], "index": "pypi", - "version": "==0.25.2" + "version": "==0.25.3" }, "pandas-ods-reader": { "hashes": [ @@ -505,59 +578,114 @@ "index": "pypi", "version": "==6.2.1" }, + "progressbar2": { + "hashes": [ + "sha256:7538d02045a1fd3aa2b2834bfda463da8755bd3ff050edc6c5ddff3bc616215f", + "sha256:eb774d1e0d03ea4730f381c13c2c6ae7abb5ddfb14d8321d7a58a61aa708f0d0" + ], + "version": "==3.47.0" + }, "psutil": { "hashes": [ - "sha256:028a1ec3c6197eadd11e7b46e8cc2f0720dc18ac6d7aabdb8e8c0d6c9704f000", - "sha256:12542c3642909f4cd1928a2fba59e16fa27e47cbeea60928ebb62a8cbd1ce123", - "sha256:503e4b20fa9d3342bcf58191bbc20a4a5ef79ca7df8972e6197cc14c5513e73d", - "sha256:863a85c1c0a5103a12c05a35e59d336e1d665747e531256e061213e2e90f63f3", - "sha256:954f782608bfef9ae9f78e660e065bd8ffcfaea780f9f2c8a133bb7cb9e826d7", - "sha256:b6e08f965a305cd84c2d07409bc16fbef4417d67b70c53b299116c5b895e3f45", - "sha256:bc96d437dfbb8865fc8828cf363450001cb04056bbdcdd6fc152c436c8a74c61", - "sha256:cf49178021075d47c61c03c0229ac0c60d5e2830f8cab19e2d88e579b18cdb76", - "sha256:d5350cb66690915d60f8b233180f1e49938756fb2d501c93c44f8fb5b970cc63", - "sha256:eba238cf1989dfff7d483c029acb0ac4fcbfc15de295d682901f0e2497e6781a" + "sha256:021d361439586a0fd8e64f8392eb7da27135db980f249329f1a347b9de99c695", + "sha256:145e0f3ab9138165f9e156c307100905fd5d9b7227504b8a9d3417351052dc3d", + "sha256:348ad4179938c965a27d29cbda4a81a1b2c778ecd330a221aadc7bd33681afbd", + "sha256:3feea46fbd634a93437b718518d15b5dd49599dfb59a30c739e201cc79bb759d", + "sha256:474e10a92eeb4100c276d4cc67687adeb9d280bbca01031a3e41fb35dfc1d131", + "sha256:47aeb4280e80f27878caae4b572b29f0ec7967554b701ba33cd3720b17ba1b07", + "sha256:73a7e002781bc42fd014dfebb3fc0e45f8d92a4fb9da18baea6fb279fbc1d966", + "sha256:d051532ac944f1be0179e0506f6889833cf96e466262523e57a871de65a15147", + "sha256:dfb8c5c78579c226841908b539c2374da54da648ee5a837a731aa6a105a54c00", + "sha256:e3f5f9278867e95970854e92d0f5fe53af742a7fc4f2eba986943345bcaed05d", + "sha256:e9649bb8fc5cea1f7723af53e4212056a6f984ee31784c10632607f472dec5ee" ], - "version": "==5.6.3" + "version": "==5.6.5" }, "pybgpranking": { "editable": true, "git": "https://github.com/D4-project/BGP-Ranking.git/", - "ref": "4c1e9932a0c32ae4456219270faf6a8f5d370f44", + "ref": "eeed3e27cd158aa573714776bbf5609951ec4508", "subdirectory": "client" }, + "pycparser": { + "hashes": [ + "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" + ], + "version": "==2.19" + }, + "pycryptodome": { + "hashes": [ + "sha256:042ae873baadd0c33b4d699a5c5b976ade3233a979d972f98ca82314632d868c", + "sha256:0502876279772b1384b660ccc91563d04490d562799d8e2e06b411e2d81128a9", + "sha256:2de33ed0a95855735d5a0fc0c39603314df9e78ee8bbf0baa9692fb46b3b8bbb", + "sha256:319e568baf86620b419d53063b18c216abf924875966efdfe06891b987196a45", + "sha256:4372ec7518727172e1605c0843cdc5375d4771e447b8148c787b860260aae151", + "sha256:48821950ffb9c836858d8fa09d7840b6df52eadd387a3c5acece55cb387743f9", + "sha256:4b9533d4166ca07abdd49ce9d516666b1df944997fe135d4b21ac376aa624aff", + "sha256:54456cf85130e01674d21fb1ab89ffccacb138a8ade88d72fa2b0ac898d2798b", + "sha256:56fdd0e425f1b8fd3a00b6d96351f86226674974814c50534864d0124d48871f", + "sha256:57b1b707363490c495ad0eeb38bd1b0e1697c497af25fad78d3a1ebf0477fd5b", + "sha256:5c485ed6e9718ebcaa81138fa70ace9c563d202b56a8cee119b4085b023931f5", + "sha256:63c103a22cbe9752f6ea9f1a0de129995bad91c4d03a66c67cffcf6ee0c9f1e1", + "sha256:68fab8455efcbfe87c5d75015476f9b606227ffe244d57bfd66269451706e899", + "sha256:6c2720696b10ae356040e888bde1239b8957fe18885ccf5e7b4e8dec882f0856", + "sha256:72166c2ac520a5dbd2d90208b9c279161ec0861662a621892bd52fb6ca13ab91", + "sha256:7c52308ac5b834331b2f107a490b2c27de024a229b61df4cdc5c131d563dfe98", + "sha256:87d8d85b4792ca5e730fb7a519fbc3ed976c59dcf79c5204589c59afd56b9926", + "sha256:896e9b6fd0762aa07b203c993fbbee7a1f1a4674c6886afd7bfa86f3d1be98a8", + "sha256:8a799bea3c6617736e914a2e77c409f52893d382f619f088f8a80e2e21f573c1", + "sha256:9d9945ac8375d5d8e60bd2a2e1df5882eaa315522eedf3ca868b1546dfa34eba", + "sha256:9ef966c727de942de3e41aa8462c4b7b4bca70f19af5a3f99e31376589c11aac", + "sha256:a168e73879619b467072509a223282a02c8047d932a48b74fbd498f27224aa04", + "sha256:a30f501bbb32e01a49ef9e09ca1260e5ab49bf33a257080ec553e08997acc487", + "sha256:a8ca2450394d3699c9f15ef25e8de9a24b401933716a1e39d37fa01f5fe3c58b", + "sha256:aec4d42deb836b8fb3ba32f2ba1ef0d33dd3dc9d430b1479ee7a914490d15b5e", + "sha256:b4af098f2a50f8d048ab12cabb59456585c0acf43d90ee79782d2d6d0ed59dba", + "sha256:b55c60c321ac91945c60a40ac9896ac7a3d432bb3e8c14006dfd82ad5871c331", + "sha256:c53348358408d94869059e16fba5ff3bef8c52c25b18421472aba272b9bb450f", + "sha256:cbfd97f9e060f0d30245cd29fa267a9a84de9da97559366fca0a3f7655acc63f", + "sha256:d3fe3f33ad52bf0c19ee6344b695ba44ffbfa16f3c29ca61116b48d97bd970fb", + "sha256:e3a79a30d15d9c7c284a7734036ee8abdb5ca3a6f5774d293cdc9e1358c1dc10", + "sha256:eec0689509389f19875f66ae8dedd59f982240cdab31b9f78a8dc266011df93a" + ], + "version": "==3.9.4" + }, "pycryptodomex": { "hashes": [ - "sha256:020928b2831b2047288c9143f41c6690eb669d60761c7ca8c5ca743a2c51517c", - "sha256:0ce1950ba6544eca4d6fd7386e2502d4bd871fcbd5e5b977604f48ea37b29fc6", - "sha256:0d5b1159a24a56fd3359b7b1aa1e4331c394033eababb2972bb923d6767968db", - "sha256:11453e8628cdccbcb08e04405298d659c0c0458cf9bf23eaaa3c201f8d635389", - "sha256:22e050089f60e70b97909fe62612ee9589f0be1c928c2aa637f2534eddf61632", - "sha256:27317f1e8e496a2f208b1c40da425d5fe760b494f95c847bb7c3074c95a8edcb", - "sha256:32e2fe1d0c5fada45b22b647f88367b210dfea40a5cc849b142b4e9fa497c488", - "sha256:3a998b390a80fd0d22c7d9fbaf49a9a11772ef90495a8baecdea2e6d09929937", - "sha256:46dda35fbed5426794ab64d483d6257dc43f52e78ba934563492df7cb89f7de6", - "sha256:4846ca0f2363bdb934c556667b056331d4aabd48f20924b0c5583a49d764d3fc", - "sha256:550f5e6f07b091f986023f871fa8a2bde9875ccae51d4bd07b31fa9855fe994f", - "sha256:561905b459de41c3ad19912cdcd88c8e24295d01e97b7b2a63d4188c8e4e0dbc", - "sha256:5745ca86a4e88a775b7cace28b947a86661d5cc09ecc1c8d97293a7d20c1bb79", - "sha256:5c2a3bb28dde992f97d856937e973dda0462bf3acb7d0009308a81159a35323b", - "sha256:73a8acc8ff7f09d482e481757d92a250f803e66e0f248019df90a69e61840180", - "sha256:8601613ebc329b853e466f581ad1156638989926e0dcdf52952542a89883836c", - "sha256:8b604f4fa1de456d6d19771b01c2823675a75a2c60e51a6b738f71fdfe865370", - "sha256:96f8622cb8061f4aca95e52cc835659f024bc2e237ee6a9d01117873b7490b98", - "sha256:a01c99532c5f7ab96274b5c9f3e135315b79b55ba5c8233fc4d029e0369e94df", - "sha256:c63040e0313e27b62b0f4295f41adecf96cde7ff4d49f653b81b1958cb1180bf", - "sha256:c812cb9f3af63da8eaa251e7e48f8b38c4e40974d2bdae2f0ca7a7a12549727a", - "sha256:cb9e8ef672b7a961f90e0a497718e0f052f76324f216840a4ec30248e4d19f20", - "sha256:ce8edda46374c344de87089f9887ad4dd317bb4a22f91f1844202eaf14b08de0", - "sha256:de58de0d5f2fb9253707ee718e1378f2194fdd394cdbed1b6464ab44642f5217", - "sha256:e0100f9b93d0119d846a33e6cb5001ee208519b81c6acf76da614b71de75885b", - "sha256:e530b77bdff5c2bf3065e6a088e1602ad193b43e285bac196d4b8820308ec6bb", - "sha256:f048069aa7b530f1c5e84d55c2b28ca7a7272bb3b8d28829d454a94bec6529a8", - "sha256:f6a9271c842e93c349b6007676a62d03dca712c9f4dff66c3270d50504ca9014" + "sha256:0943b65fb41b7403a9def6214061fdd9ab9afd0bbc581e553c72eebe60bded36", + "sha256:0a1dbb5c4d975a4ea568fb7686550aa225d94023191fb0cca8747dc5b5d77857", + "sha256:0f43f1608518347fdcb9c8f443fa5cabedd33f94188b13e4196a3a7ba90d169c", + "sha256:11ce5fec5990e34e3981ed14897ba601c83957b577d77d395f1f8f878a179f98", + "sha256:17a09e38fdc91e4857cf5a7ce82f3c0b229c3977490f2146513e366923fc256b", + "sha256:22d970cee5c096b9123415e183ae03702b2cd4d3ba3f0ced25c4e1aba3967167", + "sha256:2a1793efcbae3a2264c5e0e492a2629eb10d895d6e5f17dbbd00eb8b489c6bda", + "sha256:30a8a148a0fe482cec1aaf942bbd0ade56ec197c14fe058b2a94318c57e1f991", + "sha256:32fbbaf964c5184d3f3e349085b0536dd28184b02e2b014fc900f58bbc126339", + "sha256:347d67faee36d449dc9632da411cc318df52959079062627f1243001b10dc227", + "sha256:45f4b4e5461a041518baabc52340c249b60833aa84cea6377dc8016a2b33c666", + "sha256:4717daec0035034b002d31c42e55431c970e3e38a78211f43990e1b7eaf19e28", + "sha256:51a1ac9e7dda81da444fed8be558a60ec88dfc73b2aa4b0efa310e87acb75838", + "sha256:53e9dcc8f14783f6300b70da325a50ac1b0a3dbaee323bd9dc3f71d409c197a1", + "sha256:5519a2ed776e193688b7ddb61ab709303f6eb7d1237081e298283c72acc44271", + "sha256:583450e8e80a0885c453211ed2bd69ceea634d8c904f23ff8687f677fe810e95", + "sha256:60f862bd2a07133585a4fc2ce2b1a8ec24746b07ac44307d22ef2b767cb03435", + "sha256:612091f1d3c84e723bec7cb855cf77576e646045744794c9a3f75ba80737762f", + "sha256:629a87b87c8203b8789ccefc7f2f2faecd2daaeb56bdd0b4e44cd89565f2db07", + "sha256:6e56ec4c8938fb388b6f250ddd5e21c15e8f25a76e0ad0e2abae9afee09e67b4", + "sha256:8e8092651844a11ec7fa534395f3dfe99256ce4edca06f128efc9d770d6e1dc1", + "sha256:8f5f260629876603e08f3ce95c8ccd9b6b83bf9a921c41409046796267f7adc5", + "sha256:9a6b74f38613f54c56bd759b411a352258f47489bbefd1d57c930a291498b35b", + "sha256:a5a13ebb52c4cd065fb673d8c94f39f30823428a4de19e1f3f828b63a8882d1e", + "sha256:a77ca778a476829876a3a70ae880073379160e4a465d057e3c4e1c79acdf1b8a", + "sha256:a9f7be3d19f79429c2118fd61bc2ec4fa095e93b56fb3a5f3009822402c4380f", + "sha256:dc15a467c4f9e4b43748ba2f97aea66f67812bfd581818284c47cadc81d4caec", + "sha256:e13cdeea23059f7577c230fd580d2c8178e67ebe10e360041abe86c33c316f1c", + "sha256:e45b85c8521bca6bdfaf57e4987743ade53e9f03529dd3adbc9524094c6d55c4", + "sha256:e87f17867b260f57c88487f943eb4d46c90532652bb37046e764842c3b66cbb1", + "sha256:ee40a5b156f6c1192bc3082e9d73d0479904433cdda83110546cd67f5a15a5be", + "sha256:ef63ffde3b267043579af8830fc97fc3b9b8a526a24e3ba23af9989d4e9e689a" ], - "version": "==3.9.0" + "version": "==3.9.4" }, "pydnstrails": { "editable": true, @@ -587,25 +715,32 @@ "pyipasnhistory": { "editable": true, "git": "https://github.com/D4-project/IPASN-History.git/", - "ref": "283539cfbbde4bb54497726634407025f7d685c2", + "ref": "fc5e48608afc113e101ca6421bf693b7b9753f9e", "subdirectory": "client" }, "pymisp": { "editable": true, "git": "https://github.com/MISP/PyMISP.git", - "ref": "87fd06a8893feafaffd461d6d611be4d02e5a4a2" + "ref": "b1818b1751021fc82805524706352b0a8eb77249" }, "pyonyphe": { "editable": true, "git": "https://github.com/sebdraven/pyonyphe", "ref": "cbb0168d5cb28a9f71f7ab3773164a7039ccdb12" }, + "pyopenssl": { + "hashes": [ + "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504", + "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507" + ], + "version": "==19.1.0" + }, "pyparsing": { "hashes": [ - "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", - "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4" + "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", + "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" ], - "version": "==2.4.2" + "version": "==2.4.5" }, "pypdns": { "hashes": [ @@ -637,10 +772,10 @@ }, "python-dateutil": { "hashes": [ - "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", - "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], - "version": "==2.8.0" + "version": "==2.8.1" }, "python-docx": { "hashes": [ @@ -656,6 +791,13 @@ "index": "pypi", "version": "==0.6.18" }, + "python-utils": { + "hashes": [ + "sha256:34aaf26b39b0b86628008f2ae0ac001b30e7986a8d303b61e1357dfcdad4f6d3", + "sha256:e25f840564554eaded56eaa395bca507b0b9e9f0ae5ecb13a8cb785305c56d25" + ], + "version": "==2.3.0" + }, "pytz": { "hashes": [ "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", @@ -747,6 +889,9 @@ "version": "==3.5.32" }, "requests": { + "extras": [ + "security" + ], "hashes": [ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" @@ -763,31 +908,37 @@ }, "shodan": { "hashes": [ - "sha256:9d8bb822738d02a63dbe890b46f511f0df13fd33a60b754278c3bf5dd5cf9fc4" + "sha256:2efe383eeb083eb67137a00cc6fc5ea1fd848ce8053dfdea6696bc6ec05f6e98" ], "index": "pypi", - "version": "==1.19.0" + "version": "==1.20.0" }, "sigmatools": { "hashes": [ - "sha256:a78c0ea52ecf0016b1f1c5155fa46a23541f121e1778a1de927d9d6591535817" + "sha256:f3ffb4ad034c68c30299d2082490ffdbde9fdc1e8aa7fda26fd22a8679d2a226" ], "index": "pypi", - "version": "==0.13" + "version": "==0.14" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" + }, + "socketio-client": { + "hashes": [ + "sha256:540d8ab209154d1d9cdb97c170c589a14f7d7f17e19c14e2f59f0307e6175485" + ], + "version": "==0.5.6" }, "soupsieve": { "hashes": [ - "sha256:605f89ad5fdbfefe30cdc293303665eff2d188865d4dbe4eb510bba1edfbfce3", - "sha256:b91d676b330a0ebd5b21719cb6e9b57c57d433671f65b9c28dd3461d9a1ed0b6" + "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5", + "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda" ], - "version": "==1.9.4" + "version": "==1.9.5" }, "sparqlwrapper": { "hashes": [ @@ -807,9 +958,9 @@ }, "tabulate": { "hashes": [ - "sha256:d0097023658d4dea848d6ae73af84532d1e86617ac0925d1adf1dd903985dac3" + "sha256:5470cc6687a091c7042cee89b2946d9235fe9f6d49c193a4ae2ac7bf386737c8" ], - "version": "==0.8.5" + "version": "==0.8.6" }, "tornado": { "hashes": [ @@ -839,10 +990,10 @@ }, "urllib3": { "hashes": [ - "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", - "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" + "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", + "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" ], - "version": "==1.25.6" + "version": "==1.25.7" }, "uwhois": { "editable": true, @@ -873,6 +1024,13 @@ "index": "pypi", "version": "==0.5.7" }, + "websocket-client": { + "hashes": [ + "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9", + "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a" + ], + "version": "==0.56.0" + }, "wrapt": { "hashes": [ "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" @@ -889,10 +1047,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:00e9c337589ec67a69f1220f47409146ab1affd8eb1e8eaad23f35685bd23e47", - "sha256:5a5e2195a4672d17db79839bbdf1006a521adb57eaceea1c335ae4b3d19f088f" + "sha256:027fa3d22ccfb5da5d77c29ed740aece286a9a6cc101b564f2f7ca11eb1d490b", + "sha256:5d480cee5babf3865227d5c81269d96be8e87914fc96403ca6fa1b1e4f64c080" ], - "version": "==1.2.2" + "version": "==1.2.6" }, "yara-python": { "hashes": [ @@ -1036,6 +1194,7 @@ "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" ], + "markers": "python_version < '3.8'", "version": "==0.23" }, "mccabe": { @@ -1098,20 +1257,23 @@ }, "pyparsing": { "hashes": [ - "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", - "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4" + "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", + "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" ], - "version": "==2.4.2" + "version": "==2.4.5" }, "pytest": { "hashes": [ - "sha256:27abc3fef618a01bebb1f0d6d303d2816a99aa87a5968ebc32fe971be91eb1e6", - "sha256:58cee9e09242937e136dbb3dab466116ba20d6b7828c7620f23947f37eb4dae4" + "sha256:8e256fe71eb74e14a4d20a5987bb5e1488f0511ee800680aaedc62b9358714e8", + "sha256:ff0090819f669aaa0284d0f4aad1a6d9d67a6efdc6dd4eb4ac56b704f890a0d6" ], "index": "pypi", - "version": "==5.2.2" + "version": "==5.2.4" }, "requests": { + "extras": [ + "security" + ], "hashes": [ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" @@ -1121,17 +1283,17 @@ }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" }, "urllib3": { "hashes": [ - "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", - "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" + "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", + "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" ], - "version": "==1.25.6" + "version": "==1.25.7" }, "wcwidth": { "hashes": [ diff --git a/REQUIREMENTS b/REQUIREMENTS index 43c8896..65c0921 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -11,6 +11,7 @@ aiohttp==3.4.4 apiosintDS==1.8.1 antlr4-python3-runtime==4.7.2 ; python_version >= '3' +assemblyline_client async-timeout==3.0.1 attrs==19.1.0 backscatter==0.2.4 From ef6542c62939c465e26f55f000a578c4ff6d1791 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 20 Nov 2019 09:48:27 -0500 Subject: [PATCH 011/220] add: Added documentation and description in readme for the AssemblyLine submit module --- README.md | 1 + doc/README.md | 22 +++++++++++++++++++++- doc/expansion/assemblyline_submit.json | 9 +++++++++ doc/expansion/joesandbox_submit.json | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 doc/expansion/assemblyline_submit.json diff --git a/README.md b/README.md index 1bc8d1f..8aed0b2 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj ### Expansion modules * [apiosintDS](misp_modules/modules/expansion/apiosintds.py) - a hover and expansion module to query the OSINT.digitalside.it API. +* [AssemblyLine submit](misp_modules/modules/expansion/assemblyline_submit.py) - an expansion module to submit samples and urls to AssemblyLine. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. diff --git a/doc/README.md b/doc/README.md index 7cf7a7c..520e8f7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -22,6 +22,26 @@ On demand query API for OSINT.digitalside.it project. ----- +#### [assemblyline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_submit.py) + + + +A module to submit samples and URLs to AssemblyLine for advanced analysis, and return the link of the submission. +- **features**: +>The module requires the address of the AssemblyLine server you want to query as well as your credentials in this instance. Credentials include the user-ID and an API key or the password associated to the user-ID. +> +>If the sample or url is correctly submitted, you get then the link of the submission. +- **input**: +>Sample, url (or domain) to submit to AssemblyLine. +- **output**: +>Link of the report generated in AssemblyLine. +- **references**: +>https://www.cyber.gc.ca/en/assemblyline +- **requirements**: +>assemblyline_client: Python library to query the AssemblyLine rest API. + +----- + #### [backscatter_io](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/backscatter_io.py) @@ -536,7 +556,7 @@ A module to submit files or URLs to Joe Sandbox for an advanced analysis, and re - **input**: >Sample, url (or domain) to submit to Joe Sandbox for an advanced analysis. - **output**: ->Link of the data in input submitted to Joe Sandbox. +>Link of the report generated in Joe Sandbox. - **references**: >https://www.joesecurity.org, https://www.joesandbox.com/ - **requirements**: diff --git a/doc/expansion/assemblyline_submit.json b/doc/expansion/assemblyline_submit.json new file mode 100644 index 0000000..66bf7cc --- /dev/null +++ b/doc/expansion/assemblyline_submit.json @@ -0,0 +1,9 @@ +{ + "description": "A module to submit samples and URLs to AssemblyLine for advanced analysis, and return the link of the submission.", + "logo": "logos/assemblyline.png", + "requirements": ["assemblyline_client: Python library to query the AssemblyLine rest API."], + "input": "Sample, url (or domain) to submit to AssemblyLine.", + "output": "Link of the report generated in AssemblyLine.", + "references": ["https://www.cyber.gc.ca/en/assemblyline"], + "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials in this instance. Credentials include the user-ID and an API key or the password associated to the user-ID.\n\nIf the sample or url is correctly submitted, you get then the link of the submission." +} diff --git a/doc/expansion/joesandbox_submit.json b/doc/expansion/joesandbox_submit.json index ce0cb1f..ad59239 100644 --- a/doc/expansion/joesandbox_submit.json +++ b/doc/expansion/joesandbox_submit.json @@ -3,7 +3,7 @@ "logo": "logos/joesandbox.png", "requirements": ["jbxapi: Joe Sandbox API python3 library"], "input": "Sample, url (or domain) to submit to Joe Sandbox for an advanced analysis.", - "output": "Link of the data in input submitted to Joe Sandbox.", + "output": "Link of the report generated in Joe Sandbox.", "references": ["https://www.joesecurity.org", "https://www.joesandbox.com/"], "features": "The module requires a Joe Sandbox API key to submit files or URL, and returns the link of the submitted analysis.\n\nIt is then possible, when the analysis is completed, to query the Joe Sandbox API to get the data related to the analysis, using the [joesandbox_query module](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) directly on this submission link." } From 4e98c3efd0d5afbe3678ace7d9cb2052843ec1b6 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 20 Nov 2019 09:52:35 -0500 Subject: [PATCH 012/220] fix: Added missing AssemblyLine logo --- doc/logos/assemblyline.png | Bin 0 -> 175511 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/logos/assemblyline.png diff --git a/doc/logos/assemblyline.png b/doc/logos/assemblyline.png new file mode 100644 index 0000000000000000000000000000000000000000..bda4518a8e21746d87999775bbc65918902a76dc GIT binary patch literal 175511 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
T#z!6;o8GgT3ad7a?3>Jw1h0i;VEvmoBcRo! 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�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 Date: Wed, 20 Nov 2019 10:13:51 -0500 Subject: [PATCH 013/220] fix: Making pep8 happy --- misp_modules/modules/expansion/assemblyline_submit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misp_modules/modules/expansion/assemblyline_submit.py b/misp_modules/modules/expansion/assemblyline_submit.py index 19f5f3c..7d7ea27 100644 --- a/misp_modules/modules/expansion/assemblyline_submit.py +++ b/misp_modules/modules/expansion/assemblyline_submit.py @@ -25,6 +25,7 @@ def parse_config(apiurl, user_id, config): error['error'] = f'Error while initiating a connection with AssemblyLine: {e.__str__()}' return error + def submit_content(client, filename, data): try: return client.submit(fname=filename, contents=data.encode()) From de8737d2f3b8e6a1c3e70b64d752a8b0af8f59ae Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 20 Nov 2019 17:35:37 -0500 Subject: [PATCH 014/220] fix: Fixed input types list since domain should not be submitted to AssemblyLine --- misp_modules/modules/expansion/assemblyline_submit.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/assemblyline_submit.py b/misp_modules/modules/expansion/assemblyline_submit.py index 7d7ea27..206f5c0 100644 --- a/misp_modules/modules/expansion/assemblyline_submit.py +++ b/misp_modules/modules/expansion/assemblyline_submit.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import json from assemblyline_client import Client, ClientError @@ -7,7 +8,7 @@ from urllib.parse import urljoin moduleinfo = {"version": 1, "author": "Christian Studer", "module-type": ["expansion"], "description": "Submit files or URLs to AssemblyLine"} moduleconfig = ["apiurl", "user_id", "apikey", "password"] -mispattributes = {"input": ["attachment", "malware-sample", "url", "domain"], +mispattributes = {"input": ["attachment", "malware-sample", "url"], "output": ["link"]} From 6dcba6c8ae4b96236c32f0ba5197f5a4ee98359e Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 20 Nov 2019 17:37:37 -0500 Subject: [PATCH 015/220] fix: Fixed AssemblyLine input description --- doc/README.md | 2 +- doc/expansion/assemblyline_submit.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.md b/doc/README.md index 520e8f7..6ab49ee 100644 --- a/doc/README.md +++ b/doc/README.md @@ -32,7 +32,7 @@ A module to submit samples and URLs to AssemblyLine for advanced analysis, and r > >If the sample or url is correctly submitted, you get then the link of the submission. - **input**: ->Sample, url (or domain) to submit to AssemblyLine. +>Sample, or url to submit to AssemblyLine. - **output**: >Link of the report generated in AssemblyLine. - **references**: diff --git a/doc/expansion/assemblyline_submit.json b/doc/expansion/assemblyline_submit.json index 66bf7cc..7c90f34 100644 --- a/doc/expansion/assemblyline_submit.json +++ b/doc/expansion/assemblyline_submit.json @@ -2,7 +2,7 @@ "description": "A module to submit samples and URLs to AssemblyLine for advanced analysis, and return the link of the submission.", "logo": "logos/assemblyline.png", "requirements": ["assemblyline_client: Python library to query the AssemblyLine rest API."], - "input": "Sample, url (or domain) to submit to AssemblyLine.", + "input": "Sample, or url to submit to AssemblyLine.", "output": "Link of the report generated in AssemblyLine.", "references": ["https://www.cyber.gc.ca/en/assemblyline"], "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials in this instance. Credentials include the user-ID and an API key or the password associated to the user-ID.\n\nIf the sample or url is correctly submitted, you get then the link of the submission." From 96712da5e0dfa2f19d6f29a63d70ef315f571095 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 21 Nov 2019 13:25:50 -0500 Subject: [PATCH 016/220] add: Module to query AssemblyLine and parse the results - Takes an AssemblyLine submission link to query the API and get the full submission report - Parses the potentially malicious files and the IPs, domains or URLs they are connecting to - Possible improvement of the parsing filters in order to include more data in the MISP event --- misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/assemblyline_query.py | 164 ++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/assemblyline_query.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 04c43e6..669fb8c 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -15,4 +15,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'qrcode', 'ocr_enrich', 'pdf_enrich', 'docx_enrich', 'xlsx_enrich', 'pptx_enrich', 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', - 'assemblyline_submit'] + 'assemblyline_submit', 'assemblyline_query'] diff --git a/misp_modules/modules/expansion/assemblyline_query.py b/misp_modules/modules/expansion/assemblyline_query.py new file mode 100644 index 0000000..83c8dac --- /dev/null +++ b/misp_modules/modules/expansion/assemblyline_query.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +import json +from assemblyline_client import Client, ClientError +from collections import defaultdict +from pymisp import MISPAttribute, MISPEvent, MISPObject +from urllib.parse import urljoin + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['link'], 'format': 'misp_standard'} + +moduleinfo = {'version': '1', 'author': 'Christian Studer', + 'description': 'Query AssemblyLine with a report URL to get the parsed data.', + 'module-type': ['expansion']} +moduleconfig = ["apiurl", "user_id", "apikey", "password"] + + +class AssemblyLineParser(): + def __init__(self): + self.misp_event = MISPEvent() + self.results = {} + self.attribute = {'to_ids': True} + self._results_mapping = {'NET_DOMAIN_NAME': 'domain', 'NET_FULL_URI': 'url', + 'NET_IP': 'ip-dst'} + self._file_mapping = {'entropy': {'type': 'float', 'object_relation': 'entropy'}, + 'md5': {'type': 'md5', 'object_relation': 'md5'}, + 'mime': {'type': 'mime-type', 'object_relation': 'mimetype'}, + 'sha1': {'type': 'sha1', 'object_relation': 'sha1'}, + 'sha256': {'type': 'sha256', 'object_relation': 'sha256'}, + 'size': {'type': 'size-in-bytes', 'object_relation': 'size-in-bytes'}, + 'ssdeep': {'type': 'ssdeep', 'object_relation': 'ssdeep'}} + + def get_submission(self, attribute, client): + sid = attribute['value'].split('=')[-1] + try: + if not client.submission.is_completed(sid): + self.results['error'] = 'Submission not completed, please try again later.' + return + except Exception as e: + self.results['error'] = f'Something went wrong while trying to check if the submission in AssemblyLine is completed: {e.__str__()}' + return + try: + submission = client.submission.full(sid) + except Exception as e: + self.results['error'] = f"Something went wrong while getting the submission from AssemblyLine: {e.__str__()}" + return + self._parse_report(submission) + + def finalize_results(self): + if 'error' in self.results: + return self.results + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object', 'Tag') if (key in event and event[key])} + return {'results': results} + + def _create_attribute(self, result, attribute_type): + attribute = MISPAttribute() + attribute.from_dict(type=attribute_type, value=result['value'], **self.attribute) + if result['classification'] != 'UNCLASSIFIED': + attribute.add_tag(result['classification'].lower()) + self.misp_event.add_attribute(**attribute) + return {'referenced_uuid': attribute.uuid, 'relationship_type': '-'.join(result['context'].lower().split(' '))} + + def _create_file_object(self, file_info): + file_object = MISPObject('file') + filename_attribute = {'type': 'filename'} + filename_attribute.update(self.attribute) + if file_info['classification'] != "UNCLASSIFIED": + tag = {'Tag': [{'name': file_info['classification'].lower()}]} + filename_attribute.update(tag) + for feature, attribute in self._file_mapping.items(): + attribute.update(tag) + file_object.add_attribute(value=file_info[feature], **attribute) + return filename_attribute, file_object + for feature, attribute in self._file_mapping.items(): + file_object.add_attribute(value=file_info[feature], **attribute) + return filename_attribute, file_object + + @staticmethod + def _get_results(submission_results): + results = defaultdict(list) + for k, values in submission_results.items(): + h = k.split('.')[0] + for t in values['result']['tags']: + if t['context'] is not None: + results[h].append(t) + return results + + def _get_scores(self, file_tree): + scores = {} + for h, f in file_tree.items(): + score = f['score'] + if score > 0: + scores[h] = {'name': f['name'], 'score': score} + if f['children']: + scores.update(self._get_scores(f['children'])) + return scores + + def _parse_report(self, submission): + if submission['classification'] != 'UNCLASSIFIED': + self.misp_event.add_tag(submission['classification'].lower()) + filtered_results = self._get_results(submission['results']) + scores = self._get_scores(submission['file_tree']) + for h, results in filtered_results.items(): + if h in scores: + attribute, file_object = self._create_file_object(submission['file_infos'][h]) + print(file_object) + for filename in scores[h]['name']: + file_object.add_attribute('filename', value=filename, **attribute) + for reference in self._parse_results(results): + file_object.add_reference(**reference) + self.misp_event.add_object(**file_object) + + def _parse_results(self, results): + references = [] + for result in results: + try: + attribute_type = self._results_mapping[result['type']] + except KeyError: + continue + references.append(self._create_attribute(result, attribute_type)) + return references + + +def parse_config(apiurl, user_id, config): + error = {"error": "Please provide your AssemblyLine API key or Password."} + if config.get('apikey'): + try: + return Client(apiurl, apikey=(user_id, config['apikey'])) + except ClientError as e: + error['error'] = f'Error while initiating a connection with AssemblyLine: {e.__str__()}' + if config.get('password'): + try: + return Client(apiurl, auth=(user_id, config['password'])) + except ClientError as e: + error['error'] = f'Error while initiating a connection with AssemblyLine: {e.__str__()}' + return error + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if not request.get('config'): + return {"error": "Missing configuration."} + if not request['config'].get('apiurl'): + return {"error": "No AssemblyLine server address provided."} + apiurl = request['config']['apiurl'] + if not request['config'].get('user_id'): + return {"error": "Please provide your AssemblyLine User ID."} + user_id = request['config']['user_id'] + client = parse_config(apiurl, user_id, request['config']) + if isinstance(client, dict): + return client + assemblyline_parser = AssemblyLineParser() + assemblyline_parser.get_submission(request['attribute'], client) + return assemblyline_parser.finalize_results() + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From bf1ba161af1c4ccec9eff737cde0783b36fb89f2 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 21 Nov 2019 15:47:06 -0500 Subject: [PATCH 017/220] add: Added documentation for the AssemblyLine query module --- README.md | 1 + doc/README.md | 24 +++++++++++++++++++++++- doc/expansion/assemblyline_query.json | 9 +++++++++ doc/expansion/assemblyline_submit.json | 2 +- 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 doc/expansion/assemblyline_query.json diff --git a/README.md b/README.md index 8aed0b2..43db730 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [apiosintDS](misp_modules/modules/expansion/apiosintds.py) - a hover and expansion module to query the OSINT.digitalside.it API. * [AssemblyLine submit](misp_modules/modules/expansion/assemblyline_submit.py) - an expansion module to submit samples and urls to AssemblyLine. +* [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. diff --git a/doc/README.md b/doc/README.md index 6ab49ee..4d2ed90 100644 --- a/doc/README.md +++ b/doc/README.md @@ -22,13 +22,35 @@ On demand query API for OSINT.digitalside.it project. ----- +#### [assemblyline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_query.py) + + + +A module tu query the AssemblyLine API with a submission ID to get the submission report and parse it. +- **features**: +>The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the used-ID and an API key or the password associated to the user-ID. +> +>The submission ID extracted from the submission link is then used to query AssemblyLine and get the full submission report. This report is parsed to extract file objects and the associated IPs, domains or URLs the files are connecting to. +> +>Some more data may be parsed in the future. +- **input**: +>Link of an AssemblyLine submission report. +- **output**: +>MISP attributes & objects parsed from the AssemblyLine submission. +- **references**: +>https://www.cyber.cg.ca/en/assemblyline +- **requirements**: +>assemblyline_client: Python library to query the AssemblyLine rest API. + +----- + #### [assemblyline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_submit.py) A module to submit samples and URLs to AssemblyLine for advanced analysis, and return the link of the submission. - **features**: ->The module requires the address of the AssemblyLine server you want to query as well as your credentials in this instance. Credentials include the user-ID and an API key or the password associated to the user-ID. +>The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the user-ID and an API key or the password associated to the user-ID. > >If the sample or url is correctly submitted, you get then the link of the submission. - **input**: diff --git a/doc/expansion/assemblyline_query.json b/doc/expansion/assemblyline_query.json new file mode 100644 index 0000000..700bde0 --- /dev/null +++ b/doc/expansion/assemblyline_query.json @@ -0,0 +1,9 @@ +{ + "description": "A module tu query the AssemblyLine API with a submission ID to get the submission report and parse it.", + "logo": "logos/assemblyline.png", + "requirements": ["assemblyline_client: Python library to query the AssemblyLine rest API."], + "input": "Link of an AssemblyLine submission report.", + "output": "MISP attributes & objects parsed from the AssemblyLine submission.", + "references": ["https://www.cyber.cg.ca/en/assemblyline"], + "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the used-ID and an API key or the password associated to the user-ID.\n\nThe submission ID extracted from the submission link is then used to query AssemblyLine and get the full submission report. This report is parsed to extract file objects and the associated IPs, domains or URLs the files are connecting to.\n\nSome more data may be parsed in the future." +} diff --git a/doc/expansion/assemblyline_submit.json b/doc/expansion/assemblyline_submit.json index 7c90f34..9fe9af6 100644 --- a/doc/expansion/assemblyline_submit.json +++ b/doc/expansion/assemblyline_submit.json @@ -5,5 +5,5 @@ "input": "Sample, or url to submit to AssemblyLine.", "output": "Link of the report generated in AssemblyLine.", "references": ["https://www.cyber.gc.ca/en/assemblyline"], - "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials in this instance. Credentials include the user-ID and an API key or the password associated to the user-ID.\n\nIf the sample or url is correctly submitted, you get then the link of the submission." + "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the user-ID and an API key or the password associated to the user-ID.\n\nIf the sample or url is correctly submitted, you get then the link of the submission." } From ccf12a225c7212f2d67abec2250d16788d9c7a70 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 21 Nov 2019 17:50:49 -0500 Subject: [PATCH 018/220] fix: Making pep8 happy --- misp_modules/modules/expansion/assemblyline_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/assemblyline_query.py b/misp_modules/modules/expansion/assemblyline_query.py index 83c8dac..226e4dd 100644 --- a/misp_modules/modules/expansion/assemblyline_query.py +++ b/misp_modules/modules/expansion/assemblyline_query.py @@ -3,7 +3,6 @@ import json from assemblyline_client import Client, ClientError from collections import defaultdict from pymisp import MISPAttribute, MISPEvent, MISPObject -from urllib.parse import urljoin misperrors = {'error': 'Error'} mispattributes = {'input': ['link'], 'format': 'misp_standard'} @@ -155,6 +154,7 @@ def handler(q=False): assemblyline_parser.get_submission(request['attribute'], client) return assemblyline_parser.finalize_results() + def introspection(): return mispattributes From e4830cb71479223185dc289009370efc02ab051e Mon Sep 17 00:00:00 2001 From: AaronK Date: Fri, 22 Nov 2019 21:44:12 +0100 Subject: [PATCH 019/220] Update README.md fixes #351 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 43db730..d1289bc 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,10 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj ## How to install and start MISP modules in a Python virtualenv? (recommended) ~~~~bash -sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev tesseract-ocr libpoppler-cpp-dev imagemagick virtualenv libopencv-dev zbar-tools libzbar0 libzbar-dev libfuzzy-dev -y +sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev tesseract-ocr libpoppler-cpp-dev imagemagick virtualenv libopencv-dev zbar-tools libzbar0 libzbar-dev libfuzzy-dev build-essential -y sudo -u www-data virtualenv -p python3 /var/www/MISP/venv cd /usr/local/src/ +chown -R www-data . sudo git clone https://github.com/MISP/misp-modules.git cd misp-modules sudo -u www-data /var/www/MISP/venv/bin/pip install -I -r REQUIREMENTS From 5350003e3af3aa2e66afc3495b876b4a7cbefcfd Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 18:52:39 +0100 Subject: [PATCH 020/220] initial version of the ransomcoindb expansion module --- misp_modules/modules/expansion/__init__.py | 2 +- .../expansion/_ransomcoindb/ransomcoindb.py | 92 +++++++++++++++++++ .../modules/expansion/ransomcoindb.py | 62 +++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) create mode 100755 misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py create mode 100644 misp_modules/modules/expansion/ransomcoindb.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 669fb8c..892f3bf 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -15,4 +15,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'qrcode', 'ocr_enrich', 'pdf_enrich', 'docx_enrich', 'xlsx_enrich', 'pptx_enrich', 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', - 'assemblyline_submit', 'assemblyline_query'] + 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb'] diff --git a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py new file mode 100755 index 0000000..7225c47 --- /dev/null +++ b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +import requests +import logging +import os +import pprint + +copyright = """ + Copyright 2019 (C) by Aaron Kaplan , all rights reserved. + This file is part of the ransomwarecoindDB project and licensed under the AGPL 3.0 license +""" + +__version__ = 0.1 + + +baseurl = "https://ransomcoindb.concinnity-risks.com/api/v1/" +urls = {'BTC': {'btc' : baseurl + 'bin2btc/', + 'md5' : baseurl + 'bin2btc/md5/', + 'sha1' : baseurl + 'bin2btc/sha1/', + 'sha256': baseurl + 'bin2btc/sha256/', + }, + 'XMR': {'xmr' : baseurl + 'bin2crypto/XMR/', + 'md5' : baseurl + 'bin2crypto/XMR/md5/', + 'sha1' : baseurl + 'bin2crypto/XMR/sha1/', + 'sha256': baseurl + 'bin2crypto/XMR/sha256/', + } + } + + +def get_data_by(coin: str, key: str, value: str, api_key: str): + """ + Abstract function to fetch data from the bin2btc/{key} endpoint. + This function must be made concrete by generating a relevant function. + See below for examples. + """ + + pprint.pprint("api-key: %s" % api_key) + + headers = {'x-api-key': api_key, 'content-type': 'application/json'} + # check first if valid: + valid_coins = ['BTC', 'XMR'] + valid_keys = ['btc', 'md5', 'sha1', 'sha256'] + if coin not in valid_coins or key not in valid_keys: + logging.error("get_data_by_X(): not a valid key parameter. Must be a valid coin (i.e. from %r) and one of: %r" % (valid_coins, valid_keys)) + return None + try: + + url = urls[coin.upper()][key] + logging.debug("url = %s" % url) + if not url: + logging.error("Could not find a valid coin/key combination. Must be a valid coin (i.e. from %r) and one of: %r" % (valid_coins, valid_keys)) + return None + r = requests.get(url + "%s" % (value), headers=headers) + except Exception as ex: + logging.error("could not fetch from the service. Error: %s" % str(ex)) + + if r.status_code != 200: + logging.error("could not fetch from the service. Status code: %s" % + r.status_code) + return r.json() + + +def get_bin2btc_by_btc(btc_addr: str, api_key: str): + """ Function to fetch the data from the bin2btc/{btc} endpoint """ + return get_data_by('BTC', 'btc', btc_addr, api_key) + + +def get_bin2btc_by_md5(md5: str, api_key: str): + """ Function to fetch the data from the bin2btc/{md5} endpoint """ + return get_data_by('BTC', 'md5', md5, api_key) + + +def get_bin2btc_by_sha1(sha1: str, api_key: str): + """ Function to fetch the data from the bin2btc/{sha1} endpoint """ + return get_data_by('BTC', 'sha1', sha1, api_key) + + +def get_bin2btc_by_sha256(sha256: str, api_key: str): + """ Function to fetch the data from the bin2btc/{sha256} endpoint """ + return get_data_by('BTC', 'sha256', sha256, api_key) + + +if __name__ == "__main__": + """ Just for testing on the cmd line. """ + to_btc = "1KnuC7FdhGuHpvFNxtBpz299Q5QteUdNCq" + api_key = os.getenv('api_key') + r = get_bin2btc_by_btc(to_btc, api_key) + print(r) + r = get_bin2btc_by_md5("abc", api_key) + print(r) + r = get_data_by('XMR', 'md5', "452878CD7", api_key) + print(r) diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py new file mode 100644 index 0000000..ed0e118 --- /dev/null +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -0,0 +1,62 @@ +import json +from _ransomcoindb import ransomcoindb +import pprint + +copyright = """ + Copyright 2019 (C) by Aaron Kaplan , all rights reserved. + This file is part of the ransomwarecoindDB project and licensed under the AGPL 3.0 license +""" + +__version__ = 0.1 + + +debug=False + +misperrors = {'error': 'Error'} +# mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc', 'xmr', 'dash' ], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} +mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} +moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (ransomcoindb.metadata.li)', 'module-type': ['expansion', 'hover']} +moduleconfig = ['api-key'] + + +def handler(q=False): + """ the main handler function which gets a JSON dict as input and returns a results dict """ + + if q is False: + return False + + q = json.loads(q) + api_key = q["config"]["api-key"] + r = {"results": []} + + """ the "q" query coming in should look something like this: + {'config': {'api-key': ''}, + 'md5': 'md5 or sha1 or sha256 or btc', + 'module': 'metadatali_ransomcoindb', + 'persistent': 1} + """ + + for key in ['md5', 'sha1', 'sha256', 'btc']: # later: xmr, dash + if key in q: + answer = ransomcoindb.get_data_by('BTC', key, q[key], api_key) + """ The results data type should be: + r = { 'results': [ {'types': 'md5', 'values': [ a list of all md5s or all binaries related to this btc address ] } ] } + """ + if key in ['md5', 'sha1', 'sha256']: + r['results'].append({'types': 'btc', 'values': [ a['btc'] for a in answer ]}) + elif key == 'btc': + # better: create a MISP object + r['results'].append({ 'types': 'sha1', 'values': [ a['sha1'] for a in answer ]}) + r['results'].append({ 'types': 'md5', 'values': [ a['md5'] for a in answer ]}) + r['results'].append({ 'types': 'sha256', 'values': [ a['sha256'] for a in answer ]}) + + return r + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 24ec4a0e233f43929a1e1f2faf9f13cb7836bbcd Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 18:56:12 +0100 Subject: [PATCH 021/220] remove pprint --- misp_modules/modules/expansion/ransomcoindb.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index ed0e118..cf43d44 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -1,6 +1,5 @@ import json from _ransomcoindb import ransomcoindb -import pprint copyright = """ Copyright 2019 (C) by Aaron Kaplan , all rights reserved. @@ -15,7 +14,7 @@ debug=False misperrors = {'error': 'Error'} # mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc', 'xmr', 'dash' ], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} -moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (ransomcoindb.metadata.li)', 'module-type': ['expansion', 'hover']} +moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.concinnity-risks.com)', 'module-type': ['expansion', 'hover']} moduleconfig = ['api-key'] @@ -32,7 +31,7 @@ def handler(q=False): """ the "q" query coming in should look something like this: {'config': {'api-key': ''}, 'md5': 'md5 or sha1 or sha256 or btc', - 'module': 'metadatali_ransomcoindb', + 'module': 'ransomcoindb', 'persistent': 1} """ From 132249a521f031921f1c1e5b94ed4bb6d0dfecf5 Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 19:03:13 +0100 Subject: [PATCH 022/220] mention the ransomcoindb in the README file as a new module --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d1289bc..55cf809 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. +* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](http://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. From 44130e2bf9842c03fb80245b90a873917b56df74 Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 20:51:20 +0100 Subject: [PATCH 023/220] fix url --- misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py | 2 +- misp_modules/modules/expansion/ransomcoindb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py index 7225c47..c37855a 100755 --- a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +++ b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py @@ -13,7 +13,7 @@ copyright = """ __version__ = 0.1 -baseurl = "https://ransomcoindb.concinnity-risks.com/api/v1/" +baseurl = "https://ransomcoindb.metadata.li/api/v1/" urls = {'BTC': {'btc' : baseurl + 'bin2btc/', 'md5' : baseurl + 'bin2btc/md5/', 'sha1' : baseurl + 'bin2btc/sha1/', diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index cf43d44..aecd932 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -14,7 +14,7 @@ debug=False misperrors = {'error': 'Error'} # mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc', 'xmr', 'dash' ], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} -moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.concinnity-risks.com)', 'module-type': ['expansion', 'hover']} +moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.metadata.li)', 'module-type': ['expansion', 'hover']} moduleconfig = ['api-key'] From c5924aee2543b268b296a57096e636261676b63c Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 21:14:45 +0100 Subject: [PATCH 024/220] fix url again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55cf809..a6f2124 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. -* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](http://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. +* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](https://ransomcoindb.metadata.li): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. From b82716f888aa50b318aed3830708055ac4c903aa Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 22:24:14 +0100 Subject: [PATCH 025/220] Revert "fix url again" This reverts commit c5924aee2543b268b296a57096e636261676b63c. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6f2124..55cf809 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. -* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](https://ransomcoindb.metadata.li): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. +* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](http://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. From 777483838b01249ca842fccb98c6d59601db7c7d Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 22:24:57 +0100 Subject: [PATCH 026/220] Revert "fix url" This reverts commit 44130e2bf9842c03fb80245b90a873917b56df74. --- misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py | 2 +- misp_modules/modules/expansion/ransomcoindb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py index c37855a..7225c47 100755 --- a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +++ b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py @@ -13,7 +13,7 @@ copyright = """ __version__ = 0.1 -baseurl = "https://ransomcoindb.metadata.li/api/v1/" +baseurl = "https://ransomcoindb.concinnity-risks.com/api/v1/" urls = {'BTC': {'btc' : baseurl + 'bin2btc/', 'md5' : baseurl + 'bin2btc/md5/', 'sha1' : baseurl + 'bin2btc/sha1/', diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index aecd932..cf43d44 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -14,7 +14,7 @@ debug=False misperrors = {'error': 'Error'} # mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc', 'xmr', 'dash' ], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} -moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.metadata.li)', 'module-type': ['expansion', 'hover']} +moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.concinnity-risks.com)', 'module-type': ['expansion', 'hover']} moduleconfig = ['api-key'] From 65469055377634881cfa23e83e6fab224b4d8007 Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Mon, 25 Nov 2019 22:25:33 +0100 Subject: [PATCH 027/220] final url fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55cf809..c8000f2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. * [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. -* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](http://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. +* [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](https://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. From d73a9b601afe5096daace37e4bd32159b0b0bd26 Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Tue, 26 Nov 2019 01:08:28 +0100 Subject: [PATCH 028/220] use a helpful user-agent string --- .../modules/expansion/_ransomcoindb/ransomcoindb.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py index 7225c47..98ed588 100755 --- a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +++ b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py @@ -3,7 +3,7 @@ import requests import logging import os -import pprint +# import pprint copyright = """ Copyright 2019 (C) by Aaron Kaplan , all rights reserved. @@ -14,6 +14,8 @@ __version__ = 0.1 baseurl = "https://ransomcoindb.concinnity-risks.com/api/v1/" +user_agent = "ransomcoindb client via python-requests/%s" % requests.__version__ + urls = {'BTC': {'btc' : baseurl + 'bin2btc/', 'md5' : baseurl + 'bin2btc/md5/', 'sha1' : baseurl + 'bin2btc/sha1/', @@ -34,9 +36,11 @@ def get_data_by(coin: str, key: str, value: str, api_key: str): See below for examples. """ - pprint.pprint("api-key: %s" % api_key) + # pprint.pprint("api-key: %s" % api_key) headers = {'x-api-key': api_key, 'content-type': 'application/json'} + headers.update({'User-Agent': user_agent}) + # check first if valid: valid_coins = ['BTC', 'XMR'] valid_keys = ['btc', 'md5', 'sha1', 'sha256'] From 06025e63d018bdb4a20fcdcdac5bfe11c1b8a25c Mon Sep 17 00:00:00 2001 From: aaronkaplan Date: Tue, 26 Nov 2019 01:52:31 +0100 Subject: [PATCH 029/220] oops , use relative import --- misp_modules/modules/expansion/ransomcoindb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index cf43d44..d9d7535 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -1,5 +1,5 @@ import json -from _ransomcoindb import ransomcoindb +from ._ransomcoindb import ransomcoindb copyright = """ Copyright 2019 (C) by Aaron Kaplan , all rights reserved. From 5d7a8295832282a6a2076c6f044e47379c94c201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 26 Nov 2019 11:15:47 +0100 Subject: [PATCH 030/220] chg: Use MISPObject in ransomcoindb --- .../expansion/_ransomcoindb/ransomcoindb.py | 14 +++---- .../modules/expansion/ransomcoindb.py | 37 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py index 98ed588..26cd2e3 100755 --- a/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +++ b/misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py @@ -14,16 +14,16 @@ __version__ = 0.1 baseurl = "https://ransomcoindb.concinnity-risks.com/api/v1/" -user_agent = "ransomcoindb client via python-requests/%s" % requests.__version__ +user_agent = "ransomcoindb client via python-requests/%s" % requests.__version__ -urls = {'BTC': {'btc' : baseurl + 'bin2btc/', - 'md5' : baseurl + 'bin2btc/md5/', - 'sha1' : baseurl + 'bin2btc/sha1/', +urls = {'BTC': {'btc': baseurl + 'bin2btc/', + 'md5': baseurl + 'bin2btc/md5/', + 'sha1': baseurl + 'bin2btc/sha1/', 'sha256': baseurl + 'bin2btc/sha256/', }, - 'XMR': {'xmr' : baseurl + 'bin2crypto/XMR/', - 'md5' : baseurl + 'bin2crypto/XMR/md5/', - 'sha1' : baseurl + 'bin2crypto/XMR/sha1/', + 'XMR': {'xmr': baseurl + 'bin2crypto/XMR/', + 'md5': baseurl + 'bin2crypto/XMR/md5/', + 'sha1': baseurl + 'bin2crypto/XMR/sha1/', 'sha256': baseurl + 'bin2crypto/XMR/sha256/', } } diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index d9d7535..3bac983 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -1,5 +1,6 @@ import json from ._ransomcoindb import ransomcoindb +from pymisp import MISPObject copyright = """ Copyright 2019 (C) by Aaron Kaplan , all rights reserved. @@ -9,11 +10,11 @@ copyright = """ __version__ = 0.1 -debug=False +debug = False misperrors = {'error': 'Error'} # mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc', 'xmr', 'dash' ], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} -mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext']} +mispattributes = {'input': ['sha1', 'sha256', 'md5', 'btc'], 'output': ['btc', 'sha1', 'sha256', 'md5', 'freetext'], 'format': 'misp_standard'} moduleinfo = {'version': __version__, 'author': 'Aaron Kaplan', 'description': 'Module to access the ransomcoinDB (see https://ransomcoindb.concinnity-risks.com)', 'module-type': ['expansion', 'hover']} moduleconfig = ['api-key'] @@ -34,21 +35,23 @@ def handler(q=False): 'module': 'ransomcoindb', 'persistent': 1} """ - - for key in ['md5', 'sha1', 'sha256', 'btc']: # later: xmr, dash - if key in q: - answer = ransomcoindb.get_data_by('BTC', key, q[key], api_key) - """ The results data type should be: - r = { 'results': [ {'types': 'md5', 'values': [ a list of all md5s or all binaries related to this btc address ] } ] } - """ - if key in ['md5', 'sha1', 'sha256']: - r['results'].append({'types': 'btc', 'values': [ a['btc'] for a in answer ]}) - elif key == 'btc': - # better: create a MISP object - r['results'].append({ 'types': 'sha1', 'values': [ a['sha1'] for a in answer ]}) - r['results'].append({ 'types': 'md5', 'values': [ a['md5'] for a in answer ]}) - r['results'].append({ 'types': 'sha256', 'values': [ a['sha256'] for a in answer ]}) - + attribute = q['attribute'] + answer = ransomcoindb.get_data_by('BTC', attribute['type'], attribute['value'], api_key) + """ The results data type should be: + r = { 'results': [ {'types': 'md5', 'values': [ a list of all md5s or all binaries related to this btc address ] } ] } + """ + if attribute['type'] in ['md5', 'sha1', 'sha256']: + r['results'].append({'types': 'btc', 'values': [a['btc'] for a in answer]}) + elif attribute['type'] == 'btc': + # better: create a MISP object + files = [] + for a in answer: + obj = MISPObject('file') + obj.add_attribute('md5', a['md5']) + obj.add_attribute('sha1', a['sha1']) + obj.add_attribute('sha256', a['sha256']) + files.append(obj) + r['results'] = {'Object': [json.loads(f.to_json()) for f in files]} return r From 7a7b3a0ae11f886070dd682f3df09de927417a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 26 Nov 2019 13:27:19 +0100 Subject: [PATCH 031/220] chg: Bump dependencies --- Pipfile | 2 +- Pipfile.lock | 212 +++++++++++++++++++++++---------------------------- 2 files changed, 97 insertions(+), 117 deletions(-) diff --git a/Pipfile b/Pipfile index 415178b..1cb0889 100644 --- a/Pipfile +++ b/Pipfile @@ -11,7 +11,7 @@ flake8 = "*" [packages] dnspython = "*" -requests = "*" +requests = {extras = ["security"],version = "*"} urlarchiver = "*" passivetotal = "*" pypdns = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 8d6be41..e0e8023 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "28bab177e7e34c6b7fe8bfd8be6fe79a87ec6ca9c44ca63148fed9433d09cf21" + "sha256": "2cd074bb42f3fbefc9eefdcd673817af96b25fdf8e7e7a149878b7ae8bbfcc66" }, "pipfile-spec": 6, "requires": { @@ -325,35 +325,35 @@ }, "lxml": { "hashes": [ - "sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", - "sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", - "sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", - "sha256:1409b14bf83a7d729f92e2a7fbfe7ec929d4883ca071b06e95c539ceedb6497c", - "sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", - "sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", - "sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", - "sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d", - "sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916", - "sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0", - "sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27", - "sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", - "sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", - "sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", - "sha256:9277562f175d2334744ad297568677056861070399cec56ff06abbe2564d1232", - "sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", - "sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", - "sha256:ae88588d687bd476be588010cbbe551e9c2872b816f2da8f01f6f1fda74e1ef0", - "sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", - "sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", - "sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", - "sha256:c7fccd08b14aa437fe096c71c645c0f9be0655a9b1a4b7cffc77bcb23b3d61d2", - "sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", - "sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", - "sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", - "sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681" + "sha256:00ac0d64949fef6b3693813fe636a2d56d97a5a49b5bbb86e4cc4cc50ebc9ea2", + "sha256:0571e607558665ed42e450d7bf0e2941d542c18e117b1ebbf0ba72f287ad841c", + "sha256:0e3f04a7615fdac0be5e18b2406529521d6dbdb0167d2a690ee328bef7807487", + "sha256:13cf89be53348d1c17b453867da68704802966c433b2bb4fa1f970daadd2ef70", + "sha256:217262fcf6a4c2e1c7cb1efa08bd9ebc432502abc6c255c4abab611e8be0d14d", + "sha256:223e544828f1955daaf4cefbb4853bc416b2ec3fd56d4f4204a8b17007c21250", + "sha256:277cb61fede2f95b9c61912fefb3d43fbd5f18bf18a14fae4911b67984486f5d", + "sha256:3213f753e8ae86c396e0e066866e64c6b04618e85c723b32ecb0909885211f74", + "sha256:4690984a4dee1033da0af6df0b7a6bde83f74e1c0c870623797cec77964de34d", + "sha256:4fcc472ef87f45c429d3b923b925704aa581f875d65bac80f8ab0c3296a63f78", + "sha256:61409bd745a265a742f2693e4600e4dbd45cc1daebe1d5fad6fcb22912d44145", + "sha256:678f1963f755c5d9f5f6968dded7b245dd1ece8cf53c1aa9d80e6734a8c7f41d", + "sha256:6c6d03549d4e2734133badb9ab1c05d9f0ef4bcd31d83e5d2b4747c85cfa21da", + "sha256:6e74d5f4d6ecd6942375c52ffcd35f4318a61a02328f6f1bd79fcb4ffedf969e", + "sha256:7b4fc7b1ecc987ca7aaf3f4f0e71bbfbd81aaabf87002558f5bc95da3a865bcd", + "sha256:7ed386a40e172ddf44c061ad74881d8622f791d9af0b6f5be20023029129bc85", + "sha256:8f54f0924d12c47a382c600c880770b5ebfc96c9fd94cf6f6bdc21caf6163ea7", + "sha256:ad9b81351fdc236bda538efa6879315448411a81186c836d4b80d6ca8217cdb9", + "sha256:bbd00e21ea17f7bcc58dccd13869d68441b32899e89cf6cfa90d624a9198ce85", + "sha256:c3c289762cc09735e2a8f8a49571d0e8b4f57ea831ea11558247b5bdea0ac4db", + "sha256:cf4650942de5e5685ad308e22bcafbccfe37c54aa7c0e30cd620c2ee5c93d336", + "sha256:cfcbc33c9c59c93776aa41ab02e55c288a042211708b72fdb518221cc803abc8", + "sha256:e301055deadfedbd80cf94f2f65ff23126b232b0d1fea28f332ce58137bcdb18", + "sha256:ebbfe24df7f7b5c6c7620702496b6419f6a9aa2fd7f005eb731cc80d7b4692b9", + "sha256:eff69ddbf3ad86375c344339371168640951c302450c5d3e9936e98d6459db06", + "sha256:f6ed60a62c5f1c44e789d2cf14009423cb1646b44a43e40a9cf6a21f077678a1" ], "index": "pypi", - "version": "==4.4.1" + "version": "==4.4.2" }, "maclookup": { "hashes": [ @@ -382,37 +382,25 @@ }, "multidict": { "hashes": [ - "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", - "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3", - "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef", - "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b", - "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73", - "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc", - "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3", - "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd", - "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351", - "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941", - "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d", - "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1", - "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b", - "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a", - "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3", - "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7", - "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0", - "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0", - "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014", - "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5", - "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036", - "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d", - "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a", - "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce", - "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1", - "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a", - "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9", - "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7", - "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b" + "sha256:07f9a6bf75ad675d53956b2c6a2d4ef2fa63132f33ecc99e9c24cf93beb0d10b", + "sha256:0ffe4d4d28cbe9801952bfb52a8095dd9ffecebd93f84bdf973c76300de783c5", + "sha256:1b605272c558e4c659dbaf0fb32a53bfede44121bcf77b356e6e906867b958b7", + "sha256:205a011e636d885af6dd0029e41e3514a46e05bb2a43251a619a6e8348b96fc0", + "sha256:250632316295f2311e1ed43e6b26a63b0216b866b45c11441886ac1543ca96e1", + "sha256:2bc9c2579312c68a3552ee816311c8da76412e6f6a9cf33b15152e385a572d2a", + "sha256:318aadf1cfb6741c555c7dd83d94f746dc95989f4f106b25b8a83dfb547f2756", + "sha256:42cdd649741a14b0602bf15985cad0dd4696a380081a3319cd1ead46fd0f0fab", + "sha256:5159c4975931a1a78bf6602bbebaa366747fce0a56cb2111f44789d2c45e379f", + "sha256:87e26d8b89127c25659e962c61a4c655ec7445d19150daea0759516884ecb8b4", + "sha256:891b7e142885e17a894d9d22b0349b92bb2da4769b4e675665d0331c08719be5", + "sha256:8d919034420378132d074bf89df148d0193e9780c9fe7c0e495e895b8af4d8a2", + "sha256:9c890978e2b37dd0dc1bd952da9a5d9f245d4807bee33e3517e4119c48d66f8c", + "sha256:a37433ce8cdb35fc9e6e47e1606fa1bfd6d70440879038dca7d8dd023197eaa9", + "sha256:c626029841ada34c030b94a00c573a0c7575fe66489cde148785b6535397d675", + "sha256:cfec9d001a83dc73580143f3c77e898cf7ad78b27bb5e64dbe9652668fcafec7", + "sha256:efaf1b18ea6c1f577b1371c0159edbe4749558bfe983e13aa24d0a0c01e1ad7b" ], - "version": "==4.5.2" + "version": "==4.6.1" }, "np": { "hashes": [ @@ -462,36 +450,35 @@ }, "opencv-python": { "hashes": [ - "sha256:01505b131dc35f60e99a5da98b77156e37f872ae0ff5596e5e68d526bb572d3c", - "sha256:0478a1037505ddde312806c960a5e8958d2cf7a2885e8f2f5dde74c4028e0b04", - "sha256:17810b89f9ef8e8537e75332acf533e619e26ccadbf1b73f24bf338f2d327ddd", - "sha256:19ad2ea9fb32946761b47b9d6eed51876a8329da127f27788263fecd66651ba0", - "sha256:1a250edb739baf3e7c25d99a2ee252aac4f59a97e0bee39237eaa490fd0281d3", - "sha256:3505468970448f66cd776cb9e179570c87988f94b5cf9bcbc4c2d88bd88bbdf1", - "sha256:4e04a91da157885359f487534433340b2d709927559c80acf62c28167e59be02", - "sha256:5a49cffcdec5e37217672579c3343565926d999642844efa9c6a031ed5f32318", - "sha256:604b2ce3d4a86480ced0813da7fba269b4605ad9fea26cd2144d8077928d4b49", - "sha256:61cbb8fa9565a0480c46028599431ad8f19181a7fac8070a700515fd54cd7377", - "sha256:62d7c6e511c9454f099616315c695d02a584048e1affe034b39160db7a2ae34d", - "sha256:6555272dd9efd412d17cdc1a4f4c2da5753c099d95d9ff01aca54bb9782fb5cf", - "sha256:67d994c6b2b14cb9239e85dc7dfa6c08ef7cf6eb4def80c0af6141dfacc8cbb9", - "sha256:68c9cbe538666c4667523821cc56caee49389bea06bae4c0fc2cd68bd264226a", - "sha256:822ad8f628a9498f569c57d30865f5ef9ee17824cee0a1d456211f742028c135", - "sha256:82d972429eb4fee22c1dc4204af2a2e981f010e5e4f66daea2a6c68381b79184", - "sha256:9128924f5b58269ee221b8cf2d736f31bd3bb0391b92ee8504caadd68c8176a2", - "sha256:9172cf8270572c494d8b2ae12ef87c0f6eed9d132927e614099f76843b0c91d7", - "sha256:952bce4d30a8287b17721ddaad7f115dab268efee8576249ddfede80ec2ce404", - "sha256:a8147718e70b1f170a3d26518e992160137365a4db0ed82a9efd3040f9f660d4", - "sha256:bfdb636a3796ff223460ea0fcfda906b3b54f4bef22ae433a5b67e66fab00b25", - "sha256:c9c3f27867153634e1083390920067008ebaaab78aeb09c4e0274e69746cb2c8", - "sha256:d69be21973d450a4662ae6bd1b3df6b1af030e448d7276380b0d1adf7c8c2ae6", - "sha256:db1479636812a6579a3753b72a6fefaa73190f32bf7b19e483f8bc750cebe1a5", - "sha256:db8313d755962a7dd61e5c22a651e0743208adfdb255c6ec8904ce9cb02940c6", - "sha256:e4625a6b032e7797958aeb630d6e3e91e3896d285020aae612e6d7b342d6dfea", - "sha256:e8397a26966a1290836a52c34b362aabc65a422b9ffabcbbdec1862f023ccab8" + "sha256:04bec0a6d3a00360a7fb769b755ff4489a4ac8291821b785151f63e6d8bb59ea", + "sha256:1a2d1801c038f055852bd2379186ca8b19b4ea24afb0b8410293bc802211579b", + "sha256:1c7d235faef511aca7669f1aa650897b6c058dfde6412ea3fc58feb0fce78814", + "sha256:22c2ee5f97f85903bfb28c056566b2ecaa1d2f804b880ab39ebf94528a402992", + "sha256:25127990671dc8bd27ae8b880d7a39f9aae863052a8fbebe8977c6ce8e5fc0c9", + "sha256:3cef82b6a1f748d2f4527f5932a86d54ebd10bd89f6cf59b003c36b1015055f7", + "sha256:499a0413e7110a934ab56e635252a4c86f8be64de59f94a62318a7b895dc809e", + "sha256:5f2cf5a0ab244a0a1dbe5ec426c277b55e06ac6a472ad61be77ef643a238cbd3", + "sha256:5fec35916a6b9ce935f2e2806084303fd4e3fbb0c973a8db8f54b5aca54613cb", + "sha256:6183c9c7fab4590e0651bc941cde780988c3ad9889bd62de19d581a6f59523ea", + "sha256:67a236db8db84d7fb0f6e127f360ce6669350ef324839132e22879ec90588dab", + "sha256:6c32d36f52a6e0c02d1ab0bb95223cb4dd5525a7e8292a747116126b3d34c578", + "sha256:73a467a78ffd902d2c0265ab6b2e2cdda423d61b3d08685e0c7d0b4572142ff1", + "sha256:76de8a247970d150b1672c6646cda91217d562682e713721fc9b9bf1434553c4", + "sha256:919d5c3ec1a62258ba8c68b869b1056186e2355c4474739b199c295547e66cc1", + "sha256:982d4e80c14356098cde57a6c7d18fe0928a1c3118675bac2252ef38f152e1ab", + "sha256:9d025e6bf2989bcbc7744c26d8bd90c2629a92d8de3ba2416f62ce2a94615dd9", + "sha256:bb59f98205cd81e29f45eed043cf0f98531486dc0b3f671c9e06fecf08f7ccef", + "sha256:c8119248457e909dcd7b598621ed1d139419d69377e8cb4e2b2c49c819de287d", + "sha256:ce7b1f25be04b04f2e678b2bf23a975137f77406dcee66a88a2daeb77cda3e76", + "sha256:d64428bf59ab4d27620b00a2ad6fea2b4d62016a17849c82a7517ec12db97d55", + "sha256:e2ffa3161b8662112f1880734e8b9549d0c9e818e59f652a9d1c5bf31e36586a", + "sha256:e6fc00ac42c800fad5fb3927cfb9bf4e60bb3302cb9805f45b826d5d2546119a", + "sha256:e793df2e12093b3a01006b5b27f321e306193c7a5c9e2a6c8bf652e1ad2d6a86", + "sha256:eae543b3e9253ff702103333aabd87736b5ed5e46ab834d8e0b929f08f494dee", + "sha256:f0af656402b73ead2d9f593c2774c04b01e2d0c63e4f99e0dc2f3fde99be22b4" ], "index": "pypi", - "version": "==4.1.1.26" + "version": "==4.1.2.30" }, "pandas": { "hashes": [ @@ -587,19 +574,19 @@ }, "psutil": { "hashes": [ - "sha256:021d361439586a0fd8e64f8392eb7da27135db980f249329f1a347b9de99c695", - "sha256:145e0f3ab9138165f9e156c307100905fd5d9b7227504b8a9d3417351052dc3d", - "sha256:348ad4179938c965a27d29cbda4a81a1b2c778ecd330a221aadc7bd33681afbd", - "sha256:3feea46fbd634a93437b718518d15b5dd49599dfb59a30c739e201cc79bb759d", - "sha256:474e10a92eeb4100c276d4cc67687adeb9d280bbca01031a3e41fb35dfc1d131", - "sha256:47aeb4280e80f27878caae4b572b29f0ec7967554b701ba33cd3720b17ba1b07", - "sha256:73a7e002781bc42fd014dfebb3fc0e45f8d92a4fb9da18baea6fb279fbc1d966", - "sha256:d051532ac944f1be0179e0506f6889833cf96e466262523e57a871de65a15147", - "sha256:dfb8c5c78579c226841908b539c2374da54da648ee5a837a731aa6a105a54c00", - "sha256:e3f5f9278867e95970854e92d0f5fe53af742a7fc4f2eba986943345bcaed05d", - "sha256:e9649bb8fc5cea1f7723af53e4212056a6f984ee31784c10632607f472dec5ee" + "sha256:094f899ac3ef72422b7e00411b4ed174e3c5a2e04c267db6643937ddba67a05b", + "sha256:10b7f75cc8bd676cfc6fa40cd7d5c25b3f45a0e06d43becd7c2d2871cbb5e806", + "sha256:1b1575240ca9a90b437e5a40db662acd87bbf181f6aa02f0204978737b913c6b", + "sha256:21231ef1c1a89728e29b98a885b8e0a8e00d09018f6da5cdc1f43f988471a995", + "sha256:28f771129bfee9fc6b63d83a15d857663bbdcae3828e1cb926e91320a9b5b5cd", + "sha256:70387772f84fa5c3bb6a106915a2445e20ac8f9821c5914d7cbde148f4d7ff73", + "sha256:b560f5cd86cf8df7bcd258a851ca1ad98f0d5b8b98748e877a0aec4e9032b465", + "sha256:b74b43fecce384a57094a83d2778cdfc2e2d9a6afaadd1ebecb2e75e0d34e10d", + "sha256:e85f727ffb21539849e6012f47b12f6dd4c44965e56591d8dec6e8bc9ab96f4a", + "sha256:fd2e09bb593ad9bdd7429e779699d2d47c1268cbde4dda95fcd1bd17544a0217", + "sha256:ffad8eb2ac614518bbe3c0b8eb9dffdb3a8d2e3a7d5da51c5b974fb723a5c5aa" ], - "version": "==5.6.5" + "version": "==5.6.7" }, "pybgpranking": { "editable": true, @@ -721,7 +708,7 @@ "pymisp": { "editable": true, "git": "https://github.com/MISP/PyMISP.git", - "ref": "b1818b1751021fc82805524706352b0a8eb77249" + "ref": "a32256f1959cc3fb6a4481b77dbe2589385e4f5b" }, "pyonyphe": { "editable": true, @@ -759,9 +746,9 @@ }, "pyrsistent": { "hashes": [ - "sha256:eb6545dbeb1aa69ab1fb4809bfbf5a8705e44d92ef8fc7c2361682a47c46c778" + "sha256:f3b280d030afb652f79d67c5586157c5c1355c9a58dfc7940566e28d28f3df1b" ], - "version": "==0.15.5" + "version": "==0.15.6" }, "pytesseract": { "hashes": [ @@ -951,10 +938,10 @@ }, "stix2-patterns": { "hashes": [ - "sha256:137cbe28d29af774d526a49d60b3a40af7c19fe1e5f252e741bb25f253d5616f" + "sha256:1a583ec394af0c61eaa36efeef06e33d03bb7aae8b6e2f491449d5f220dc819d" ], "index": "pypi", - "version": "==1.1.0" + "version": "==1.2.0" }, "tabulate": { "hashes": [ @@ -1094,13 +1081,6 @@ } }, "develop": { - "atomicwrites": { - "hashes": [ - "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", - "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" - ], - "version": "==1.3.0" - }, "attrs": { "hashes": [ "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", @@ -1229,10 +1209,10 @@ }, "pluggy": { "hashes": [ - "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", - "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34" + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], - "version": "==0.13.0" + "version": "==0.13.1" }, "py": { "hashes": [ @@ -1264,11 +1244,11 @@ }, "pytest": { "hashes": [ - "sha256:8e256fe71eb74e14a4d20a5987bb5e1488f0511ee800680aaedc62b9358714e8", - "sha256:ff0090819f669aaa0284d0f4aad1a6d9d67a6efdc6dd4eb4ac56b704f890a0d6" + "sha256:1897d74f60a5d8be02e06d708b41bf2445da2ee777066bd68edf14474fc201eb", + "sha256:f6a567e20c04259d41adce9a360bd8991e6aa29dd9695c5e6bd25a9779272673" ], "index": "pypi", - "version": "==5.2.4" + "version": "==5.3.0" }, "requests": { "extras": [ From 9744c1e0a5ddc9bab447fa89d0c1a680ac5263cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 26 Nov 2019 17:49:01 +0100 Subject: [PATCH 032/220] Revert "Merge pull request #341 from StefanKelm/master" This reverts commit 1df0d9152ed3346a9432393177c89e137bfc0c64, reversing changes made to 6042619c6b7fb40fd77b5328f933e67e839e1e83. This PR was a fixing a typo in a test case. The typo is in a 3rd party service. --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index ee1c267..d9ce6f1 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -91,7 +91,7 @@ class TestExpansions(unittest.TestCase): def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} response = self.misp_modules_post(query) - self.assertEqual(self.get_values(response), '1es14c7qlb5cyhlmuekctxlgc1fv2ti9da fraudulent bitcoin address') + self.assertEqual(self.get_values(response), '1es14c7qlb5cyhlmuekctxlgc1fv2ti9da fraudolent bitcoin address') def test_countrycode(self): query = {"module": "countrycode", "domain": "www.circl.lu"} From f7495785255feb80fa0d9a08b6c465594aaa66e1 Mon Sep 17 00:00:00 2001 From: Stefano Ortolani Date: Thu, 21 Nov 2019 12:10:29 +0000 Subject: [PATCH 033/220] add: Modules to query/import/submit data from/to Lastline --- README.md | 3 + doc/README.md | 52 ++ doc/expansion/lastline_query.json | 9 + doc/expansion/lastline_submit.json | 9 + doc/import_mod/lastline_import.json | 9 + doc/logos/lastline.png | Bin 0 -> 7194 bytes misp_modules/lib/__init__.py | 2 +- misp_modules/lib/lastline_api.py | 673 ++++++++++++++++++ misp_modules/modules/expansion/__init__.py | 3 +- .../modules/expansion/lastline_query.py | 137 ++++ .../modules/expansion/lastline_submit.py | 175 +++++ misp_modules/modules/import_mod/__init__.py | 14 +- .../modules/import_mod/lastline_import.py | 151 ++++ 13 files changed, 1234 insertions(+), 3 deletions(-) create mode 100644 doc/expansion/lastline_query.json create mode 100644 doc/expansion/lastline_submit.json create mode 100644 doc/import_mod/lastline_import.json create mode 100644 doc/logos/lastline.png create mode 100644 misp_modules/lib/lastline_api.py create mode 100644 misp_modules/modules/expansion/lastline_query.py create mode 100644 misp_modules/modules/expansion/lastline_submit.py create mode 100644 misp_modules/modules/import_mod/lastline_import.py diff --git a/README.md b/README.md index c8000f2..6f56434 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [iprep](misp_modules/modules/expansion/iprep.py) - an expansion module to get IP reputation from packetmail.net. * [Joe Sandbox submit](misp_modules/modules/expansion/joesandbox_submit.py) - Submit files and URLs to Joe Sandbox. * [Joe Sandbox query](misp_modules/modules/expansion/joesandbox_query.py) - Query Joe Sandbox with the link of an analysis and get the parsed data. +* [Lastline submit](misp_modules/modules/expansion/lastline_submit.py) - Submit files and URLs to Lastline. +* [Lastline query](misp_modules/modules/expansion/lastline_query.py) - Query Lastline with the link to an analysis and parse the report. * [macaddress.io](misp_modules/modules/expansion/macaddress_io.py) - a hover module to retrieve vendor details and other information regarding a given MAC address or an OUI from [MAC address Vendor Lookup](https://macaddress.io). See [integration tutorial here](https://macaddress.io/integrations/MISP-module). * [macvendors](misp_modules/modules/expansion/macvendors.py) - a hover module to retrieve mac vendor information. * [ocr-enrich](misp_modules/modules/expansion/ocr_enrich.py) - an enrichment module to get OCRized data from images into MISP. @@ -104,6 +106,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [Email Import](misp_modules/modules/import_mod/email_import.py) Email import module for MISP to import basic metadata. * [GoAML import](misp_modules/modules/import_mod/goamlimport.py) Module to import [GoAML](http://goaml.unodc.org/goaml/en/index.html) XML format. * [Joe Sandbox import](misp_modules/modules/import_mod/joe_import.py) Parse data from a Joe Sandbox json report. +* [Lastline import](misp_modules/modules/import_mod/lastline_import.py) Module to import Lastline analysis reports. * [OCR](misp_modules/modules/import_mod/ocr.py) Optical Character Recognition (OCR) module for MISP to import attributes from images, scan or faxes. * [OpenIOC](misp_modules/modules/import_mod/openiocimport.py) OpenIOC import based on PyMISP library. * [ThreatAnalyzer](misp_modules/modules/import_mod/threatanalyzer_import.py) - An import module to process ThreatAnalyzer archive.zip/analysis.json sandbox exports. diff --git a/doc/README.md b/doc/README.md index 4d2ed90..143c716 100644 --- a/doc/README.md +++ b/doc/README.md @@ -586,6 +586,41 @@ A module to submit files or URLs to Joe Sandbox for an advanced analysis, and re ----- +#### [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) + + + +Query Lastline with an analysis link and parse the report into MISP attributes and objects. +The analysis link can also be retrieved from the output of the [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) expansion module. +- **features**: +>The module uses the new format and it is able to return MISP attributes and objects. +>The module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module. +- **input**: +>Link to a Lastline analysis. +- **output**: +>MISP attributes and objects parsed from the analysis report. +- **references**: +>https://www.lastline.com + +----- + +#### [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) + + + +Module to submit a file or URL to Lastline. +- **features**: +>The module requires a Lastline API key and token (or username and password). +>When the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module. +- **input**: +>File or URL to submit to Lastline. +- **output**: +>Link to the report generated by Lastline. +- **references**: +>https://www.lastline.com + +----- + #### [macaddress_io](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/macaddress_io.py) @@ -1620,6 +1655,23 @@ A module to import data from a Joe Sandbox analysis json report. ----- +#### [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) + + + +Module to import and parse reports from Lastline analysis links. +- **features**: +>The module uses the new format and it is able to return MISP attributes and objects. +>The module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module. +- **input**: +>Link to a Lastline analysis. +- **output**: +>MISP attributes and objects parsed from the analysis report. +- **references**: +>https://www.lastline.com + +----- + #### [mispjson](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/mispjson.py) Module to import MISP JSON format for merging MISP events. diff --git a/doc/expansion/lastline_query.json b/doc/expansion/lastline_query.json new file mode 100644 index 0000000..0d5da39 --- /dev/null +++ b/doc/expansion/lastline_query.json @@ -0,0 +1,9 @@ +{ + "description": "Query Lastline with an analysis link and parse the report into MISP attributes and objects.\nThe analysis link can also be retrieved from the output of the [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) expansion module.", + "logo": "logos/lastline.png", + "requirements": [], + "input": "Link to a Lastline analysis.", + "output": "MISP attributes and objects parsed from the analysis report.", + "references": ["https://www.lastline.com"], + "features": "The module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module." +} diff --git a/doc/expansion/lastline_submit.json b/doc/expansion/lastline_submit.json new file mode 100644 index 0000000..cf5ef57 --- /dev/null +++ b/doc/expansion/lastline_submit.json @@ -0,0 +1,9 @@ +{ + "description": "Module to submit a file or URL to Lastline.", + "logo": "logos/lastline.png", + "requirements": [], + "input": "File or URL to submit to Lastline.", + "output": "Link to the report generated by Lastline.", + "references": ["https://www.lastline.com"], + "features": "The module requires a Lastline API key and token (or username and password).\nWhen the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module." +} diff --git a/doc/import_mod/lastline_import.json b/doc/import_mod/lastline_import.json new file mode 100644 index 0000000..1d4c15d --- /dev/null +++ b/doc/import_mod/lastline_import.json @@ -0,0 +1,9 @@ +{ + "description": "Module to import and parse reports from Lastline analysis links.", + "logo": "logos/lastline.png", + "requirements": [], + "input": "Link to a Lastline analysis.", + "output": "MISP attributes and objects parsed from the analysis report.", + "references": ["https://www.lastline.com"], + "features": "The module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module." +} diff --git a/doc/logos/lastline.png b/doc/logos/lastline.png new file mode 100644 index 0000000000000000000000000000000000000000..6bffe77f6a3ac58f7d405952c7df39af05be0a54 GIT binary patch literal 7194 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 literal 0 HcmV?d00001 diff --git a/misp_modules/lib/__init__.py b/misp_modules/lib/__init__.py index 0dbceb8..57a2505 100644 --- a/misp_modules/lib/__init__.py +++ b/misp_modules/lib/__init__.py @@ -1 +1 @@ -all = ['joe_parser'] +all = ['joe_parser', 'lastline_api'] diff --git a/misp_modules/lib/lastline_api.py b/misp_modules/lib/lastline_api.py new file mode 100644 index 0000000..a6912b8 --- /dev/null +++ b/misp_modules/lib/lastline_api.py @@ -0,0 +1,673 @@ +""" +Lastline Community API Client and Utilities. + +:Copyright: + Copyright 2019 Lastline, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Copyright (c) 2010-2012 by Internet Systems Consortium, Inc. ("ISC") + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +""" +import ipaddress +import logging +import re +import requests +import pymisp + +from urllib import parse + + +DEFAULT_LASTLINE_API = "https://user.lastline.com/papi" + + +HOSTED_LASTLINE_DOMAINS = frozenset([ + "user.lastline.com", + "user.emea.lastline.com", +]) + + +def purge_none(d): + """Purge None entries from a dictionary.""" + return {k: v for k, v in d.items() if v is not None} + + +def get_analysis_link(api_url, uuid): + """ + Get the analysis link of a task given the task uuid. + + :param str api_url: the URL + :param str uuid: the task uuid + :rtype: str + :return: the analysis link + """ + portal_url_path = "../portal#/analyst/task/{}/overview".format(uuid) + analysis_link = parse.urljoin(api_url, portal_url_path) + return analysis_link + + +def get_uuid_from_link(analysis_link): + """ + Return task uuid from link or raise ValueError exception. + + :param str analysis_link: a link + :rtype: str + :return: the uuid + :raises ValueError: if the link contains not task uuid + """ + try: + return re.findall("[a-fA-F0-9]{32}", analysis_link)[0] + except IndexError: + raise ValueError("Link does not contain a valid task uuid") + + +def is_analysis_hosted(analysis_link): + """ + Return whether the analysis link is pointing to a hosted submission. + + :param str analysis_link: a link + :rtype: boolean + :return: whether the link is hosted + """ + for domain in HOSTED_LASTLINE_DOMAINS: + if domain in analysis_link: + return True + return False + + +def get_api_url_from_link(analysis_link): + """ + Return the API url related to the provided analysis link. + + :param str analysis_link: a link + :rtype: str + :return: the API url + """ + parsed_uri = parse.urlparse(analysis_link) + return "{uri.scheme}://{uri.netloc}/papi".format(uri=parsed_uri) + + +class InvalidArgument(Exception): + """Error raised invalid.""" + + +class CommunicationError(Exception): + """Exception raised in case of timeouts or other network problem.""" + + +class Error(Exception): + """Generic server error.""" + + +class ApiError(Error): + """Server error with a message and an error code.""" + def __init__(self, error_msg, error_code=None): + super(ApiError, self).__init__(error_msg, error_code) + self.error_msg = error_msg + self.error_code = error_code + + def __str__(self): + if self.error_code is None: + error_code = "" + else: + error_code = " ({})".format(self.error_code) + return "{}{}".format(self.error_msg, error_code) + + +class LastlineResultBaseParser(object): + """ + This is a parser to extract *basic* information from a Lastline result dictionary. + + Note: this is a *version 0*: the information we extract is merely related to the behaviors and + the HTTP connections. Further iterations will include host activity such as files, mutexes, + registry keys, strings, etc. + """ + + def __init__(self): + """Constructor.""" + self.misp_event = None + + @staticmethod + def _get_mitre_techniques(result): + return [ + "misp-galaxy:mitre-attack-pattern=\"{} - {}\"".format(w[0], w[1]) + for w in sorted(set([ + (y["id"], y["name"]) + for x in result.get("malicious_activity", []) + for y in result.get("activity_to_mitre_techniques", {}).get(x, []) + ])) + ] + + def parse(self, analysis_link, result): + """ + Parse the analysis result into a MISP event. + + :param str analysis_link: the analysis link + :param dict[str, any] result: the JSON returned by the analysis client. + :rtype: MISPEvent + :return: some results that can be consumed by MIPS. + """ + self.misp_event = pymisp.MISPEvent() + + # Add analysis subject info + if "url" in result["analysis_subject"]: + o = pymisp.MISPObject("url") + o.add_attribute("url", result["analysis_subject"]["url"]) + else: + o = pymisp.MISPObject("file") + o.add_attribute("md5", type="md5", value=result["analysis_subject"]["md5"]) + o.add_attribute("sha1", type="sha1", value=result["analysis_subject"]["sha1"]) + o.add_attribute("sha256", type="sha256", value=result["analysis_subject"]["sha256"]) + o.add_attribute( + "mimetype", + type="mime-type", + value=result["analysis_subject"]["mime_type"] + ) + self.misp_event.add_object(o) + + # Add HTTP requests from url analyses + network_dict = result.get("report", {}).get("analysis", {}).get("network", {}) + for request in network_dict.get("requests", []): + parsed_uri = parse.urlparse(request["url"]) + o = pymisp.MISPObject(name='http-request') + o.add_attribute('host', parsed_uri.netloc) + o.add_attribute('method', "GET") + o.add_attribute('uri', request["url"]) + o.add_attribute("ip", request["ip"]) + self.misp_event.add_object(o) + + # Add network behaviors from files + for subject in result.get("report", {}).get("analysis_subjects", []): + + # Add DNS requests + for dns_query in subject.get("dns_queries", []): + hostname = dns_query.get("hostname") + # Skip if it is an IP address + try: + if hostname == "wpad": + continue + _ = ipaddress.ip_address(hostname) + continue + except ValueError: + pass + + o = pymisp.MISPObject(name='dns-record') + o.add_attribute('queried-domain', hostname) + self.misp_event.add_object(o) + + # Add HTTP conversations (as network connection and as http request) + for http_conversation in subject.get("http_conversations", []): + o = pymisp.MISPObject(name="network-connection") + o.add_attribute("ip-src", http_conversation["src_ip"]) + o.add_attribute("ip-dst", http_conversation["dst_ip"]) + o.add_attribute("src-port", http_conversation["src_port"]) + o.add_attribute("dst-port", http_conversation["dst_port"]) + o.add_attribute("hostname-dst", http_conversation["dst_host"]) + o.add_attribute("layer3-protocol", "IP") + o.add_attribute("layer4-protocol", "TCP") + o.add_attribute("layer7-protocol", "HTTP") + self.misp_event.add_object(o) + + method, path, http_version = http_conversation["url"].split(" ") + if http_conversation["dst_port"] == 80: + uri = "http://{}{}".format(http_conversation["dst_host"], path) + else: + uri = "http://{}:{}{}".format( + http_conversation["dst_host"], + http_conversation["dst_port"], + path + ) + o = pymisp.MISPObject(name='http-request') + o.add_attribute('host', http_conversation["dst_host"]) + o.add_attribute('method', method) + o.add_attribute('uri', uri) + o.add_attribute('ip', http_conversation["dst_ip"]) + self.misp_event.add_object(o) + + # Add sandbox info like score and sandbox type + o = pymisp.MISPObject(name="sandbox-report") + sandbox_type = "saas" if is_analysis_hosted(analysis_link) else "on-premise" + o.add_attribute("score", result["score"]) + o.add_attribute("sandbox-type", sandbox_type) + o.add_attribute("{}-sandbox".format(sandbox_type), "lastline") + o.add_attribute("permalink", analysis_link) + self.misp_event.add_object(o) + + # Add behaviors + o = pymisp.MISPObject(name="sb-signature") + o.add_attribute("software", "Lastline") + for activity in result.get("malicious_activity", []): + a = pymisp.MISPAttribute() + a.from_dict(type="text", value=activity) + o.add_attribute("signature", **a) + self.misp_event.add_object(o) + + # Add mitre techniques + for technique in self._get_mitre_techniques(result): + self.misp_event.add_tag(technique) + + +class LastlineCommunityHTTPClient(object): + """"A very basic HTTP client providing basic functionality.""" + + @classmethod + def sanitize_login_params(cls, api_key, api_token, username, password): + """ + Return a dictionary with either API or USER credentials. + + :param str|None api_key: the API key + :param str|None api_token: the API token + :param str|None username: the username + :param str|None password: the password + :rtype: dict[str, str] + :return: the dictionary + :raises InvalidArgument: if too many values are invalid + """ + if api_key and api_token: + return { + "api_key": api_key, + "api_token": api_token, + } + elif username and password: + return { + "username": username, + "password": password, + } + else: + raise InvalidArgument("Arguments provided do not contain valid data") + + @classmethod + def get_login_params_from_conf(cls, conf, section_name): + """ + Get the module configuration from a ConfigParser object. + + :param ConfigParser conf: the conf object + :param str section_name: the section name + :rtype: dict[str, str] + :return: the parsed configuration + """ + api_key = conf.get(section_name, "api_key", fallback=None) + api_token = conf.get(section_name, "api_token", fallback=None) + username = conf.get(section_name, "username", fallback=None) + password = conf.get(section_name, "password", fallback=None) + return cls.sanitize_login_params(api_key, api_token, username, password) + + @classmethod + def get_login_params_from_request(cls, request): + """ + Get the module configuration from a ConfigParser object. + + :param dict[str, any] request: the request object + :rtype: dict[str, str] + :return: the parsed configuration + """ + api_key = request.get("config", {}).get("api_key") + api_token = request.get("config", {}).get("api_token") + username = request.get("config", {}).get("username") + password = request.get("config", {}).get("password") + return cls.sanitize_login_params(api_key, api_token, username, password) + + def __init__(self, api_url, login_params, timeout=60, verify_ssl=True): + """ + Instantiate a Lastline mini client. + + :param str api_url: the URL of the API + :param dict[str, str]: the login parameters + :param int timeout: the timeout + :param boolean verify_ssl: whether to verify the SSL certificate + """ + self.__url = api_url + self.__login_params = login_params + self.__timeout = timeout + self.__verify_ssl = verify_ssl + self.__session = None + self.__logger = logging.getLogger(__name__) + + def __login(self): + """Login using account-based or key-based methods.""" + if self.__session is None: + self.__session = requests.session() + + login_url = "/".join([self.__url, "login"]) + try: + response = self.__session.request( + method="POST", + url=login_url, + data=self.__login_params, + verify=self.__verify_ssl, + timeout=self.__timeout, + proxies=None, + ) + except requests.RequestException as e: + raise CommunicationError(e) + + self.__handle_response(response) + + def __is_logged_in(self): + """Return whether we have an active session.""" + return self.__session is not None + + @staticmethod + def __parse_response(response): + """ + Parse the response. + + :param requests.Response response: the response + :rtype: tuple(str|None, Error|ApiError) + :return: a tuple with mutually exclusive fields (either the response or the error) + """ + try: + ret = response.json() + if "success" not in ret: + return None, Error("no success field in response") + + if not ret["success"]: + error_msg = ret.get("error", "") + error_code = ret.get("error_code", None) + return None, ApiError(error_msg, error_code) + + if "data" not in ret: + return None, Error("no data field in response") + + return ret["data"], None + except ValueError as e: + return None, Error("Response not json {}".format(e)) + + def __handle_response(self, response, raw=False): + """ + Check a response for issues and parse the return. + + :param requests.Response response: the response + :param boolean raw: whether the raw body should be returned + :rtype: str + :return: if raw, return the response content; if not raw, the data field + :raises: CommunicationError, ApiError, Error + """ + # Check for HTTP errors, and re-raise in case + try: + response.raise_for_status() + except requests.RequestException as e: + _, err = self.__parse_response(response) + if isinstance(err, ApiError): + err_msg = "{}: {}".format(e, err.error_msg) + else: + err_msg = "{}".format(e) + raise CommunicationError(err_msg) + + # Otherwise return the data (either parsed or not) but reraise if we have an API error + if raw: + return response.content + data, err = self.__parse_response(response) + if err: + raise err + return data + + def do_request( + self, + method, + module, + function, + params=None, + data=None, + files=None, + url=None, + fmt="JSON", + raw=False, + raw_response=False, + headers=None, + stream_response=False + ): + if raw_response: + raw = True + + if fmt: + fmt = fmt.lower().strip() + if fmt not in ["json", "xml", "html", "pdf"]: + raise InvalidArgument("Only json, xml, html and pdf supported") + elif not raw: + raise InvalidArgument("Unformatted response requires raw=True") + + if fmt != "json" and not raw: + raise InvalidArgument("Non-json format requires raw=True") + + if method not in ["POST", "GET"]: + raise InvalidArgument("Only POST and GET supported") + + function = function.strip(" /") + if not function: + raise InvalidArgument("No function provided") + + # Login after we verified that all arguments are fine + if not self.__is_logged_in(): + self.__login() + + url_parts = [url or self.__url] + module = module.strip(" /") + if module: + url_parts.append(module) + if fmt: + function_part = "%s.%s" % (function, fmt) + else: + function_part = function + url_parts.append(function_part) + url = "/".join(url_parts) + + try: + try: + response = self.__session.request( + method=method, + url=url, + data=data, + params=params, + files=files, + verify=self.__verify_ssl, + timeout=self.__timeout, + stream=stream_response, + headers=headers, + ) + except requests.RequestException as e: + raise CommunicationError(e) + + if raw_response: + return response + return self.__handle_response(response, raw) + + except Error as e: + raise e + + except CommunicationError as e: + raise e + + +class LastlineCommunityAPIClient(object): + """"A very basic API client providing basic functionality.""" + + def __init__(self, api_url, login_params): + """ + Instantiate the API client. + + :param str api_url: the URL to the API server + :param dict[str, str] login_params: the login parameters + """ + self._client = LastlineCommunityHTTPClient(api_url, login_params) + self._logger = logging.getLogger(__name__) + + def _post(self, module, function, params=None, data=None, files=None, fmt="JSON"): + return self._client.do_request( + method="POST", + module=module, + function=function, + params=params, + data=data, + files=files, + fmt=fmt, + ) + + def _get(self, module, function, params=None, fmt="JSON"): + return self._client.do_request( + method="GET", + module=module, + function=function, + params=params, + fmt=fmt, + ) + + def get_progress(self, uuid, analysis_instance=None): + """ + Get the completion progress of a given task. + + :param str uuid: the unique identifier of the submitted task + :param str analysis_instance: if set, defines the analysis instance to query + :rtype: dict[str, int] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + params = purge_none({"uuid": uuid, "analysis_instance": analysis_instance}) + return self._get("analysis", "get_progress", params=params) + + def get_result(self, uuid, analysis_instance=None): + """ + Get report results for a given task. + + :param str uuid: the unique identifier of the submitted task + :param str analysis_instance: if set, defines the analysis instance to query + :rtype: dict[str, any] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + params = purge_none( + { + "uuid": uuid, + "analysis_instance": analysis_instance, + "report_format": "json", + } + ) + return self._get("analysis", "get_result", params=params) + + def submit_url( + self, + url, + referer=None, + user_agent=None, + bypass_cache=False, + ): + """ + Upload an URL to be analyzed. + + :param str url: the url to analyze + :param str|None referer: the referer + :param str|None user_agent: the user agent + :param boolean bypass_cache: bypass_cache + :rtype: dict[str, any] + :return: a dictionary like the following if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + params = purge_none( + { + "url": url, + "bypass_cache": bypass_cache, + "referer": referer, + "user_agent": user_agent + } + ) + return self._post(module="analysis", function="submit_url", params=params) + + def submit_file( + self, + file_data, + file_name=None, + password=None, + analysis_env=None, + allow_network_traffic=True, + analysis_timeout=None, + bypass_cache=False, + ): + """ + Upload a file to be analyzed. + + :param bytes file_data: the data as a byte sequence + :param str|None file_name: if set, represents the name of the file to submit + :param str|None password: if set, use it to extract the sample + :param str|None analysis_env: if set, e.g windowsxp + :param boolean allow_network_traffic: if set to False, deny network connections + :param int|None analysis_timeout: if set limit the duration of the analysis + :param boolean bypass_cache: whether to re-process a file (requires special permissions) + :rtype: dict[str, any] + :return: a dictionary in the following form if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + params = purge_none( + { + "filename": file_name, + "password": password, + "analysis_env": analysis_env, + "allow_network_traffic": allow_network_traffic, + "analysis_timeout": analysis_timeout, + "bypass_cache": bypass_cache, + } + ) + files = {"file": (file_name, file_data, "application/octet-stream")} + return self._post(module="analysis", function="submit_file", params=params, files=files) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 892f3bf..91e7459 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -15,4 +15,5 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'qrcode', 'ocr_enrich', 'pdf_enrich', 'docx_enrich', 'xlsx_enrich', 'pptx_enrich', 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', - 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb'] + 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', + 'lastline_query', 'lastline_submit'] diff --git a/misp_modules/modules/expansion/lastline_query.py b/misp_modules/modules/expansion/lastline_query.py new file mode 100644 index 0000000..4019b92 --- /dev/null +++ b/misp_modules/modules/expansion/lastline_query.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +""" +Module (type "expansion") to query a Lastline report from an analysis link. +""" +import json + +import lastline_api + + +misperrors = { + "error": "Error", +} + +mispattributes = { + "input": [ + "link", + ], + "output": ["text"], + "format": "misp_standard", +} + +moduleinfo = { + "version": "0.1", + "author": "Stefano Ortolani", + "description": "Get a Lastline report from an analysis link.", + "module-type": ["expansion"], +} + +moduleconfig = [ + "api_key", + "api_token", + "username", + "password", +] + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo + + +def handler(q=False): + if q is False: + return False + + request = json.loads(q) + + # Parse the init parameters + try: + auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) + analysis_link = request['attribute']['value'] + # The API url changes based on the analysis link host name + api_url = lastline_api.get_api_url_from_link(analysis_link) + except Exception as e: + misperrors["error"] = "Error parsing configuration: {}".format(e) + return misperrors + + # Parse the call parameters + try: + task_uuid = lastline_api.get_uuid_from_link(analysis_link) + except (KeyError, ValueError) as e: + misperrors["error"] = "Error processing input parameters: {}".format(e) + return misperrors + + # Make the API calls + try: + api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + response = api_client.get_progress(task_uuid) + if response.get("completed") != 1: + raise ValueError("Analysis is not finished yet.") + + response = api_client.get_result(task_uuid) + if not response: + raise ValueError("Analysis report is empty.") + + except Exception as e: + misperrors["error"] = "Error issuing the API call: {}".format(e) + return misperrors + + # Parse and return + result_parser = lastline_api.LastlineResultBaseParser() + result_parser.parse(analysis_link, response) + + event = result_parser.misp_event + event_dictionary = json.loads(event.to_json()) + + return { + "results": { + key: event_dictionary[key] + for key in ('Attribute', 'Object', 'Tag') + if (key in event and event[key]) + } + } + + +if __name__ == "__main__": + """Test querying information from a Lastline analysis link.""" + import argparse + import configparser + + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config-file", dest="config_file") + parser.add_argument("-s", "--section-name", dest="section_name") + args = parser.parse_args() + c = configparser.ConfigParser() + c.read(args.config_file) + a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + + j = json.dumps( + { + "config": a, + "attribute": { + "value": ( + "https://user.lastline.com/portal#/analyst/task/" + "1fcbcb8f7fb400100772d6a7b62f501b/overview" + ) + } + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) + + j = json.dumps( + { + "config": a, + "attribute": { + "value": ( + "https://user.lastline.com/portal#/analyst/task/" + "f3c0ae115d51001017ff8da768fa6049/overview" + ) + } + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) diff --git a/misp_modules/modules/expansion/lastline_submit.py b/misp_modules/modules/expansion/lastline_submit.py new file mode 100644 index 0000000..0ae475a --- /dev/null +++ b/misp_modules/modules/expansion/lastline_submit.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Module (type "expansion") to submit files and URLs to Lastline for analysis. +""" +import base64 +import io +import json +import zipfile + +import lastline_api + + +misperrors = { + "error": "Error", +} + +mispattributes = { + "input": [ + "attachment", + "malware-sample", + "url", + ], + "output": [ + "link", + ], +} + +moduleinfo = { + "version": "0.1", + "author": "Stefano Ortolani", + "description": "Submit files and URLs to Lastline analyst", + "module-type": ["expansion", "hover"], +} + +moduleconfig = [ + "api_url", + "api_key", + "api_token", + "username", + "password", + # Module options + "bypass_cache", +] + + +DEFAULT_ZIP_PASSWORD = b"infected" + + +def __unzip(zipped_data, password=None): + data_file_object = io.BytesIO(zipped_data) + with zipfile.ZipFile(data_file_object) as zip_file: + sample_hashname = zip_file.namelist()[0] + data_zipped = zip_file.read(sample_hashname, password) + return data_zipped + + +def __str_to_bool(x): + return x in ("True", "true", True) + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo + + +def handler(q=False): + if q is False: + return False + + request = json.loads(q) + + # Parse the init parameters + try: + auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) + api_url = request.get("config", {}).get("api_url", lastline_api.DEFAULT_LASTLINE_API) + except Exception as e: + misperrors["error"] = "Error parsing configuration: {}".format(e) + return misperrors + + # Parse the call parameters + try: + bypass_cache = request.get("config", {}).get("bypass_cache", False) + call_args = {"bypass_cache": __str_to_bool(bypass_cache)} + if "url" in request: + # URLs are text strings + api_method = lastline_api.LastlineCommunityAPIClient.submit_url + call_args["url"] = request.get("url") + else: + data = request.get("data") + # Malware samples are zip-encrypted and then base64 encoded + if "malware-sample" in request: + api_method = lastline_api.LastlineCommunityAPIClient.submit_file + call_args["file_data"] = __unzip(base64.b64decode(data), DEFAULT_ZIP_PASSWORD) + call_args["file_name"] = request.get("malware-sample").split("|", 1)[0] + call_args["password"] = DEFAULT_ZIP_PASSWORD + # Attachments are just base64 encoded + elif "attachment" in request: + api_method = lastline_api.LastlineCommunityAPIClient.submit_file + call_args["file_data"] = base64.b64decode(data) + call_args["file_name"] = request.get("attachment") + + else: + raise ValueError("Input parameters do not specify either an URL or a file") + + except Exception as e: + misperrors["error"] = "Error processing input parameters: {}".format(e) + return misperrors + + # Make the API call + try: + api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + response = api_method(api_client, **call_args) + task_uuid = response.get("task_uuid") + if not task_uuid: + raise ValueError("Unable to process returned data") + if response.get("score") is not None: + tags = ["workflow:state='complete'"] + else: + tags = ["workflow:state='incomplete'"] + + except Exception as e: + misperrors["error"] = "Error issuing the API call: {}".format(e) + return misperrors + + # Assemble and return + analysis_link = lastline_api.get_analysis_link(api_url, task_uuid) + + return { + "results": [ + { + "types": "link", + "categories": ["External analysis"], + "values": analysis_link, + "tags": tags, + }, + ] + } + + +if __name__ == "__main__": + """Test submitting a test subject to the Lastline backend.""" + import argparse + import configparser + + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config-file", dest="config_file") + parser.add_argument("-s", "--section-name", dest="section_name") + args = parser.parse_args() + c = configparser.ConfigParser() + c.read(args.config_file) + a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + + j = json.dumps( + { + "config": a, + "url": "https://www.google.com", + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) + + with open("./tests/test_files/test.docx", "rb") as f: + data = f.read() + + j = json.dumps( + { + "config": a, + "data": base64.b64encode(data).decode("utf-8"), + "attachment": "test.docx", + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) diff --git a/misp_modules/modules/import_mod/__init__.py b/misp_modules/modules/import_mod/__init__.py index 65a7069..fbad911 100644 --- a/misp_modules/modules/import_mod/__init__.py +++ b/misp_modules/modules/import_mod/__init__.py @@ -3,4 +3,16 @@ import os import sys sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/')[:-3]))) -__all__ = ['vmray_import', 'ocr', 'cuckooimport', 'goamlimport', 'email_import', 'mispjson', 'openiocimport', 'threatanalyzer_import', 'csvimport', 'joe_import'] +__all__ = [ + 'vmray_import', + 'lastline_import', + 'ocr', + 'cuckooimport', + 'goamlimport', + 'email_import', + 'mispjson', + 'openiocimport', + 'threatanalyzer_import', + 'csvimport', + 'joe_import', +] diff --git a/misp_modules/modules/import_mod/lastline_import.py b/misp_modules/modules/import_mod/lastline_import.py new file mode 100644 index 0000000..ff26b93 --- /dev/null +++ b/misp_modules/modules/import_mod/lastline_import.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +Module (type "import") to import a Lastline report from an analysis link. +""" +import json + +import lastline_api + + +misperrors = { + "error": "Error", +} + +userConfig = { + "analysis_link": { + "type": "String", + "errorMessage": "Expected analysis link", + "message": "The link to a Lastline analysis" + }, +} + +inputSource = [] + +moduleinfo = { + "version": "0.1", + "author": "Stefano Ortolani", + "description": "Import a Lastline report from an analysis link.", + "module-type": ["import"] +} + +moduleconfig = [ + "api_key", + "api_token", + "username", + "password", +] + + +def introspection(): + modulesetup = {} + try: + userConfig + modulesetup["userConfig"] = userConfig + except NameError: + pass + try: + inputSource + modulesetup["inputSource"] = inputSource + except NameError: + pass + modulesetup["format"] = "misp_standard" + return modulesetup + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo + + +def handler(q=False): + if q is False: + return False + + request = json.loads(q) + + # Parse the init parameters + try: + auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) + analysis_link = request["config"]["analysis_link"] + # The API url changes based on the analysis link host name + api_url = lastline_api.get_api_url_from_link(analysis_link) + except Exception as e: + misperrors["error"] = "Error parsing configuration: {}".format(e) + return misperrors + + # Parse the call parameters + try: + task_uuid = lastline_api.get_uuid_from_link(analysis_link) + except (KeyError, ValueError) as e: + misperrors["error"] = "Error processing input parameters: {}".format(e) + return misperrors + + # Make the API calls + try: + api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + response = api_client.get_progress(task_uuid) + if response.get("completed") != 1: + raise ValueError("Analysis is not finished yet.") + + response = api_client.get_result(task_uuid) + if not response: + raise ValueError("Analysis report is empty.") + + except Exception as e: + misperrors["error"] = "Error issuing the API call: {}".format(e) + return misperrors + + # Parse and return + result_parser = lastline_api.LastlineResultBaseParser() + result_parser.parse(analysis_link, response) + + event = result_parser.misp_event + event_dictionary = json.loads(event.to_json()) + + return { + "results": { + key: event_dictionary[key] + for key in ("Attribute", "Object", "Tag") + if (key in event and event[key]) + } + } + + +if __name__ == "__main__": + """Test importing information from a Lastline analysis link.""" + import argparse + import configparser + + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config-file", dest="config_file") + parser.add_argument("-s", "--section-name", dest="section_name") + args = parser.parse_args() + c = configparser.ConfigParser() + c.read(args.config_file) + a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + + j = json.dumps( + { + "config": { + **a, + "analysis_link": ( + "https://user.lastline.com/portal#/analyst/task/" + "1fcbcb8f7fb400100772d6a7b62f501b/overview" + ) + } + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) + + j = json.dumps( + { + "config": { + **a, + "analysis_link": ( + "https://user.lastline.com/portal#/analyst/task/" + "f3c0ae115d51001017ff8da768fa6049/overview" + ) + } + } + ) + print(json.dumps(handler(j), indent=4, sort_keys=True)) From 2b8a2d03cdd9402da695daee3ba25fbc9cd0dcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 15:24:16 +0100 Subject: [PATCH 034/220] chg: Bump dependencies --- Pipfile.lock | 106 ++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index e0e8023..6eca5ba 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -112,10 +112,10 @@ }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "cffi": { "hashes": [ @@ -296,11 +296,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", - "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" + "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", + "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" ], "markers": "python_version < '3.8'", - "version": "==0.23" + "version": "==1.1.0" }, "isodate": { "hashes": [ @@ -375,10 +375,10 @@ }, "more-itertools": { "hashes": [ - "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", - "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", + "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" ], - "version": "==7.2.0" + "version": "==8.0.0" }, "multidict": { "hashes": [ @@ -591,7 +591,7 @@ "pybgpranking": { "editable": true, "git": "https://github.com/D4-project/BGP-Ranking.git/", - "ref": "eeed3e27cd158aa573714776bbf5609951ec4508", + "ref": "fd9c0e03af9b61d4bf0b67ac73c7208a55178a54", "subdirectory": "client" }, "pycparser": { @@ -708,7 +708,7 @@ "pymisp": { "editable": true, "git": "https://github.com/MISP/PyMISP.git", - "ref": "a32256f1959cc3fb6a4481b77dbe2589385e4f5b" + "ref": "c03b26a18c83b47d2a04e908fe6c78176ec45509" }, "pyonyphe": { "editable": true, @@ -794,21 +794,19 @@ }, "pyyaml": { "hashes": [ - "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", - "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", - "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", - "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", - "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", - "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", - "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", - "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", - "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", - "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", - "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", - "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", - "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" + "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", + "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", + "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", + "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", + "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", + "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", + "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", + "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", + "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", + "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", + "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4" ], - "version": "==5.1.2" + "version": "==5.2" }, "pyzbar": { "hashes": [ @@ -938,10 +936,10 @@ }, "stix2-patterns": { "hashes": [ - "sha256:1a583ec394af0c61eaa36efeef06e33d03bb7aae8b6e2f491449d5f220dc819d" + "sha256:a23c707e8043a7933f2858adb02e58f3bace510d331e4b7dd4f1c3cceb6c43b6" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.2.1" }, "tabulate": { "hashes": [ @@ -1058,19 +1056,25 @@ }, "yarl": { "hashes": [ - "sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9", - "sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f", - "sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb", - "sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320", - "sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842", - "sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0", - "sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829", - "sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310", - "sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4", - "sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8", - "sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1" + "sha256:031e8f56cf085d3b3df6b6bce756369ea7052b82d35ea07b6045f209c819e0e5", + "sha256:074958fe4578ef3a3d0bdaf96bbc25e4c4db82b7ff523594776fcf3d3f16c531", + "sha256:2db667ee21f620b446a54a793e467714fc5a446fcc82d93a47e8bde01d69afab", + "sha256:326f2dbaaa17b858ae86f261ae73a266fd820a561fc5142cee9d0fc58448fbd7", + "sha256:32a3885f542f74d0f4f87057050c6b45529ebd79d0639f56582e741521575bfe", + "sha256:56126ef061b913c3eefecace3404ca88917265d0550b8e32bbbeab29e5c830bf", + "sha256:589ac1e82add13fbdedc04eb0a83400db728e5f1af2bd273392088ca90de7062", + "sha256:6076bce2ecc6ebf6c92919d77762f80f4c9c6ecc9c1fbaa16567ec59ad7d6f1d", + "sha256:63be649c535d18ab6230efbc06a07f7779cd4336a687672defe70c025349a47b", + "sha256:6642cbc92eaffa586180f669adc772f5c34977e9e849e93f33dc142351e98c9c", + "sha256:6fa05a25f2280e78a514041d4609d39962e7d51525f2439db9ad7a2ae7aac163", + "sha256:7ed006a220422c33ff0889288be24db56ff0a3008ffe9eaead58a690715ad09b", + "sha256:80c9c213803b50899460cc355f47e66778c3c868f448b7b7de5b1f1858c82c2a", + "sha256:8bae18e2129850e76969b57869dacc72a66cccdbeebce1a28d7f3d439c21a7a3", + "sha256:ab112fba996a8f48f427e26969f2066d50080df0c24007a8cc6d7ae865e19013", + "sha256:b1c178ef813940c9a5cbad42ab7b8b76ac08b594b0a6bad91063c968e0466efc", + "sha256:d6eff151c3b23a56a5e4f496805619bc3bdf4f749f63a7a95ad50e8267c17475" ], - "version": "==1.3.0" + "version": "==1.4.1" }, "zipp": { "hashes": [ @@ -1090,10 +1094,10 @@ }, "certifi": { "hashes": [ - "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", - "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" + "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", + "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" ], - "version": "==2019.9.11" + "version": "==2019.11.28" }, "chardet": { "hashes": [ @@ -1171,11 +1175,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", - "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" + "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", + "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" ], "markers": "python_version < '3.8'", - "version": "==0.23" + "version": "==1.1.0" }, "mccabe": { "hashes": [ @@ -1186,10 +1190,10 @@ }, "more-itertools": { "hashes": [ - "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", - "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" + "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", + "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" ], - "version": "==7.2.0" + "version": "==8.0.0" }, "nose": { "hashes": [ @@ -1244,11 +1248,11 @@ }, "pytest": { "hashes": [ - "sha256:1897d74f60a5d8be02e06d708b41bf2445da2ee777066bd68edf14474fc201eb", - "sha256:f6a567e20c04259d41adce9a360bd8991e6aa29dd9695c5e6bd25a9779272673" + "sha256:63344a2e3bce2e4d522fd62b4fdebb647c019f1f9e4ca075debbd13219db4418", + "sha256:f67403f33b2b1d25a6756184077394167fe5e2f9d8bdaab30707d19ccec35427" ], "index": "pypi", - "version": "==5.3.0" + "version": "==5.3.1" }, "requests": { "extras": [ From 6f9544514376f7164541e0d97ef48a25f6764114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 15:25:01 +0100 Subject: [PATCH 035/220] chg: Update email import module, support objects --- .../modules/import_mod/email_import.py | 320 ++++++------------ 1 file changed, 102 insertions(+), 218 deletions(-) diff --git a/misp_modules/modules/import_mod/email_import.py b/misp_modules/modules/import_mod/email_import.py index 956f520..0ffb59b 100644 --- a/misp_modules/modules/import_mod/email_import.py +++ b/misp_modules/modules/import_mod/email_import.py @@ -3,24 +3,25 @@ import json import base64 -import io import zipfile -import codecs import re -from email import message_from_bytes -from email.utils import parseaddr -from email.iterators import typed_subpart_iterator -from email.parser import Parser from html.parser import HTMLParser -from email.header import decode_header +from pymisp.tools import EMailObject, make_binary_objects +try: + from pymisp.tools import URLObject +except ImportError: + raise ImportError('Unable to import URLObject, pyfaup missing') +from io import BytesIO +from pathlib import Path + misperrors = {'error': 'Error'} -userConfig = {} -inputSource = ['file'] +mispattributes = {'inputSource': ['file'], 'output': ['MISP objects'], + 'format': 'misp_standard'} -moduleinfo = {'version': '0.1', - 'author': 'Seamus Tuohy', +moduleinfo = {'version': '0.2', + 'author': 'Seamus Tuohy, Raphaël Vinot', 'description': 'Email import module for MISP', 'module-type': ['import']} @@ -35,93 +36,13 @@ moduleconfig = ["unzip_attachments", def handler(q=False): if q is False: return False - results = [] # Decode and parse email request = json.loads(q) # request data is always base 64 byte encoded data = base64.b64decode(request["data"]) - # Double decode to force headers to be re-parsed with proper encoding - message = Parser().parsestr(message_from_bytes(data).as_string()) - # Decode any encoded headers to get at proper string - for key, val in message.items(): - replacement = get_decoded_header(key, val) - if replacement is not None: - message.replace_header(key, replacement) - - # Extract all header information - all_headers = "" - for k, v in message.items(): - all_headers += "{0}: {1}\n".format(k.strip(), v.strip()) - results.append({"values": all_headers, "type": 'email-header'}) - - # E-Mail MIME Boundry - if message.get_boundary(): - results.append({"values": message.get_boundary(), "type": 'email-mime-boundary'}) - - # E-Mail Reply To - if message.get('In-Reply-To'): - results.append({"values": message.get('In-Reply-To').strip(), "type": 'email-reply-to'}) - - # X-Mailer - if message.get('X-Mailer'): - results.append({"values": message.get('X-Mailer'), "type": 'email-x-mailer'}) - - # Thread Index - if message.get('Thread-Index'): - results.append({"values": message.get('Thread-Index'), "type": 'email-thread-index'}) - - # Email Message ID - if message.get('Message-ID'): - results.append({"values": message.get('Message-ID'), "type": 'email-message-id'}) - - # Subject - if message.get('Subject'): - results.append({"values": message.get('Subject'), "type": 'email-subject'}) - - # Source - from_addr = message.get('From') - if from_addr: - results.append({"values": parseaddr(from_addr)[1], "type": 'email-src', "comment": "From: {0}".format(from_addr)}) - results.append({"values": parseaddr(from_addr)[0], "type": 'email-src-display-name', "comment": "From: {0}".format(from_addr)}) - - # Return Path - return_path = message.get('Return-Path') - if return_path: - # E-Mail Source - results.append({"values": parseaddr(return_path)[1], "type": 'email-src', "comment": "Return Path: {0}".format(return_path)}) - # E-Mail Source Name - results.append({"values": parseaddr(return_path)[0], "type": 'email-src-display-name', "comment": "Return Path: {0}".format(return_path)}) - - # Destinations - # Split and sort destination header values - recipient_headers = ['To', 'Cc', 'Bcc'] - - for hdr_val in recipient_headers: - if message.get(hdr_val): - addrs = message.get(hdr_val).split(',') - for addr in addrs: - # Parse and add destination header values - parsed_addr = parseaddr(addr) - results.append({"values": parsed_addr[1], "type": "email-dst", "comment": "{0}: {1}".format(hdr_val, addr)}) - results.append({"values": parsed_addr[0], "type": "email-dst-display-name", "comment": "{0}: {1}".format(hdr_val, addr)}) - - # Get E-Mail Targets - # Get the addresses that received the email. - # As pulled from the Received header - received = message.get_all('Received') - if received: - email_targets = set() - for rec in received: - try: - email_check = re.search(r"for\s(.*@.*);", rec).group(1) - email_check = email_check.strip(' <>') - email_targets.add(parseaddr(email_check)[1]) - except (AttributeError): - continue - for tar in email_targets: - results.append({"values": tar, "type": "target-email", "comment": "Extracted from email 'Received' header"}) + email_object = EMailObject(pseudofile=BytesIO(data), attach_original_mail=True, standalone=False) # Check if we were given a configuration config = request.get("config", {}) @@ -137,66 +58,81 @@ def handler(q=False): zip_pass_crack = config.get("guess_zip_attachment_passwords", None) if (zip_pass_crack is not None and zip_pass_crack.lower() in acceptable_config_yes): zip_pass_crack = True - password_list = None # Only want to collect password list once + password_list = get_zip_passwords(email_object.email) # Do we extract URL's from the email. extract_urls = config.get("extract_urls", None) if (extract_urls is not None and extract_urls.lower() in acceptable_config_yes): extract_urls = True + file_objects = [] # All possible file objects # Get Attachments # Get file names of attachments - for part in message.walk(): - filename = part.get_filename() - if filename is not None: - results.append({"values": filename, "type": 'email-attachment'}) - attachment_data = part.get_payload(decode=True) - # Base attachment data is default - attachment_files = [{"values": filename, "data": base64.b64encode(attachment_data).decode()}] - if unzip is True: # Attempt to unzip the attachment and return its files - zipped_files = ["doc", "docx", "dot", "dotx", "xls", - "xlsx", "xlm", "xla", "xlc", "xlt", - "xltx", "xlw", "ppt", "pptx", "pps", - "ppsx", "pot", "potx", "potx", "sldx", - "odt", "ods", "odp", "odg", "odf", - "fodt", "fods", "fodp", "fodg", "ott", - "uot"] + for attachment_name, attachment in email_object.attachments: + # Create file objects for the attachments + if not attachment_name: + attachment_name = 'NameMissing.txt' - zipped_filetype = False - for ext in zipped_files: - if filename.endswith(ext) is True: - zipped_filetype = True - if not zipped_filetype: - try: - attachment_files += get_zipped_contents(filename, attachment_data) - except RuntimeError: # File is encrypted with a password - if zip_pass_crack is True: - if password_list is None: - password_list = get_zip_passwords(message) - password = test_zip_passwords(attachment_data, password_list) - if password is None: # Inform the analyst that we could not crack password - attachment_files[0]['comment'] = "Encrypted Zip: Password could not be cracked from message" - else: - attachment_files[0]['comment'] = """Original Zipped Attachment with Password {0}""".format(password) - attachment_files += get_zipped_contents(filename, attachment_data, password=password) - except zipfile.BadZipFile: # Attachment is not a zipfile - pass - for attch_item in attachment_files: - attch_item["type"] = 'malware-sample' - results.append(attch_item) - else: # Check email body part for urls - if (extract_urls is True and part.get_content_type() == 'text/html'): - url_parser = HTMLURLParser() - charset = get_charset(part, get_charset(message)) - url_parser.feed(part.get_payload(decode=True).decode(charset)) - urls = url_parser.urls - for url in urls: - results.append({"values": url, "type": "url"}) - r = {'results': results} + temp_filename = Path(attachment_name) + zipped_files = ["doc", "docx", "dot", "dotx", "xls", "xlsx", "xlm", "xla", + "xlc", "xlt", "xltx", "xlw", "ppt", "pptx", "pps", "ppsx", + "pot", "potx", "potx", "sldx", "odt", "ods", "odp", "odg", + "odf", "fodt", "fods", "fodp", "fodg", "ott", "uot"] + # Attempt to unzip the attachment and return its files + if unzip and temp_filename.suffix[1:] not in zipped_files: + try: + unzip_attachement(attachment_name, attachment, email_object, file_objects) + except RuntimeError: # File is encrypted with a password + if zip_pass_crack is True: + password = test_zip_passwords(attachment, password_list) + if password: + unzip_attachement(attachment_name, attachment, email_object, file_objects, password) + else: # Inform the analyst that we could not crack password + f_object, main_object, sections = make_binary_objects(pseudofile=attachment, filename=attachment_name, standalone=False) + f_object.comment = "Encrypted Zip: Password could not be cracked from message" + file_objects.append(f_object) + file_objects.append(main_object) + file_objects += sections + email_object.add_reference(f_object.uuid, 'includes', 'Email attachment') + except zipfile.BadZipFile: # Attachment is not a zipfile + # Just straight add the file + f_object, main_object, sections = make_binary_objects(pseudofile=attachment, filename=attachment_name, standalone=False) + file_objects.append(f_object) + file_objects.append(main_object) + file_objects += sections + email_object.add_reference(f_object.uuid, 'includes', 'Email attachment') + else: + # Just straight add the file + f_object, main_object, sections = make_binary_objects(pseudofile=attachment, filename=attachment_name, standalone=False) + file_objects.append(f_object) + file_objects.append(main_object) + file_objects += sections + email_object.add_reference(f_object.uuid, 'includes', 'Email attachment') + + mail_body = email_object.email.get_body(preferencelist=('html', 'plain')) + if extract_urls: + charset = mail_body.get_content_charset() + if mail_body.get_content_type() == 'text/html': + url_parser = HTMLURLParser() + url_parser.feed(mail_body.get_payload(decode=True).decode(charset, errors='ignore')) + urls = url_parser.urls + else: + urls = re.findall(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', mail_body.get_payload(decode=True).decode(charset, errors='ignore')) + for url in urls: + if not url: + continue + url_object = URLObject(url, standalone=False) + file_objects.append(url_object) + email_object.add_reference(url_object.uuid, 'includes', 'URL in email body') + + objects = [email_object.to_json()] + if file_objects: + objects += [o.to_json() for o in file_objects if o] + r = {'results': {'Object': [json.loads(o) for o in objects]}} return r -def get_zipped_contents(filename, data, password=None): +def unzip_attachement(filename, data, email_object, file_objects, password=None): """Extract the contents of a zipfile. Args: @@ -210,17 +146,23 @@ def get_zipped_contents(filename, data, password=None): "comment":"string here"} """ - with zipfile.ZipFile(io.BytesIO(data), "r") as zf: - unzipped_files = [] + with zipfile.ZipFile(data, "r") as zf: if password is not None: + comment = f'Extracted from {filename} with password "{password}"' password = str.encode(password) # Byte encoded password required + else: + comment = f'Extracted from {filename}' for zip_file_name in zf.namelist(): # Get all files in the zip file with zf.open(zip_file_name, mode='r', pwd=password) as fp: - file_data = fp.read() - unzipped_files.append({"values": zip_file_name, - "data": base64.b64encode(file_data).decode(), # Any password works when not encrypted - "comment": "Extracted from {0}".format(filename)}) - return unzipped_files + file_data = BytesIO(fp.read()) + f_object, main_object, sections = make_binary_objects(pseudofile=file_data, + filename=zip_file_name, + standalone=False) + f_object.comment = comment + file_objects.append(f_object) + file_objects.append(main_object) + file_objects += sections + email_object.add_reference(f_object.uuid, 'includes', 'Email attachment') def test_zip_passwords(data, test_passwords): @@ -234,7 +176,7 @@ def test_zip_passwords(data, test_passwords): Returns a byte string containing a found password and None if password is not found. """ - with zipfile.ZipFile(io.BytesIO(data), "r") as zf: + with zipfile.ZipFile(data, "r") as zf: firstfile = zf.namelist()[0] for pw_test in test_passwords: byte_pwd = str.encode(pw_test) @@ -268,23 +210,16 @@ def get_zip_passwords(message): # Not checking for multi-part message because by having an # encrypted zip file it must be multi-part. - text_parts = [part for part in typed_subpart_iterator(message, 'text', 'plain')] - html_parts = [part for part in typed_subpart_iterator(message, 'text', 'html')] body = [] - # Get full message character set once - # Language example reference (using python2) - # http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual-email-with-python/ - message_charset = get_charset(message) - for part in text_parts: - charset = get_charset(part, message_charset) - body.append(part.get_payload(decode=True).decode(charset)) - for part in html_parts: - charset = get_charset(part, message_charset) - html_part = part.get_payload(decode=True).decode(charset) - html_parser = HTMLTextParser() - html_parser.feed(html_part) - for text in html_parser.text_data: - body.append(text) + for part in message.walk(): + charset = part.get_content_charset() + if part.get_content_type() == 'text/plain': + body.append(part.get_payload(decode=True).decode(charset, errors='ignore')) + elif part.get_content_type() == 'text/html': + html_parser = HTMLTextParser() + html_parser.feed(part.get_payload(decode=True).decode(charset, errors='ignore')) + for text in html_parser.text_data: + body.append(text) raw_text = "\n".join(body).strip() # Add subject to text corpus to parse @@ -334,63 +269,12 @@ class HTMLURLParser(HTMLParser): def handle_starttag(self, tag, attrs): if tag == 'a': self.urls.append(dict(attrs).get('href')) - - -def get_charset(message, default="ascii"): - """Get a message objects charset - - Args: - message (email.message): Email message object to parse. - default (string): String containing default charset to return. - """ - if message.get_content_charset(): - return message.get_content_charset() - if message.get_charset(): - return message.get_charset() - return default - - -def get_decoded_header(header, value): - subject, encoding = decode_header(value)[0] - subject = subject.strip() # extra whitespace will mess up encoding - if isinstance(subject, bytes): - # Remove Byte Order Mark (BOM) from UTF strings - if encoding == 'utf-8': - return re.sub(codecs.BOM_UTF8, b"", subject).decode(encoding) - if encoding == 'utf-16': - return re.sub(codecs.BOM_UTF16, b"", subject).decode(encoding) - elif encoding == 'utf-32': - return re.sub(codecs.BOM_UTF32, b"", subject).decode(encoding) - # Try various UTF decodings for any unknown 8bit encodings - elif encoding == 'unknown-8bit': - for enc in [('utf-8', codecs.BOM_UTF8), - ('utf-32', codecs.BOM_UTF32), # 32 before 16 so it raises errors - ('utf-16', codecs.BOM_UTF16)]: - try: - return re.sub(enc[1], b"", subject).decode(enc[0]) - except UnicodeDecodeError: - continue - # If none of those encoding work return it in RFC2047 format - return str(subject) - # Provide RFC2047 format string if encoding is a unknown encoding - # Better to have the analyst decode themselves than to provide a mangled string - elif encoding is None: - return str(subject) - else: - return subject.decode(encoding) + if tag == 'img': + self.urls.append(dict(attrs).get('src')) def introspection(): - modulesetup = {} - try: - modulesetup['userConfig'] = userConfig - except NameError: - pass - try: - modulesetup['inputSource'] = inputSource - except NameError: - pass - return modulesetup + return mispattributes def version(): From 7048f0163336a22d9bb8fe601114a022f0147ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 17:03:16 +0100 Subject: [PATCH 036/220] chg: deactive emails tests, need update --- tests/test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test.py b/tests/test.py index d32bd00..c72d236 100644 --- a/tests/test.py +++ b/tests/test.py @@ -57,6 +57,7 @@ class TestModules(unittest.TestCase): assert("mrxcls.sys" in values) assert("mdmcpq3.PNF" in values) + @unittest.skip() def test_email_headers(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -105,6 +106,7 @@ class TestModules(unittest.TestCase): self.assertEqual(types['email-reply-to'], 1) self.assertIn("", values) + @unittest.skip() def test_email_attachment_basic(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -129,6 +131,7 @@ class TestModules(unittest.TestCase): attch_data = base64.b64decode(i["data"]) self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_attachment_unpack(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -159,6 +162,8 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + + @unittest.skip() def test_email_dont_unpack_compressed_doc_attachments(self): """Ensures that compressed """ @@ -192,6 +197,7 @@ class TestModules(unittest.TestCase): self.assertEqual(filesum.hexdigest(), '098da5381a90d4a51e6b844c18a0fecf2e364813c2f8b317cfdc51c21f2506a5') + @unittest.skip() def test_email_attachment_unpack_with_password(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -220,6 +226,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_attachment_password_in_body(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -243,6 +250,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_attachment_password_in_body_quotes(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -271,6 +279,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_attachment_password_in_html_body(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -311,6 +320,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_body_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -331,6 +341,7 @@ class TestModules(unittest.TestCase): self.assertIn('results', response, "No server results found.") + @unittest.skip() def test_email_header_proper_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -395,6 +406,7 @@ class TestModules(unittest.TestCase): self.assertIn("", values) + @unittest.skip() def test_email_header_malformed_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -462,6 +474,7 @@ class TestModules(unittest.TestCase): self.assertIn("", values) + @unittest.skip() def test_email_header_CJK_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -489,6 +502,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(japanese_charset, i['values'], "Subject not properly decoded") + @unittest.skip() def test_email_malformed_header_CJK_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -519,6 +533,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(japanese_charset, i['values'], "Subject not properly decoded") + @unittest.skip() def test_email_malformed_header_emoji_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -549,6 +564,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(emoji_string, i['values'], "Subject not properly decoded") + @unittest.skip() def test_email_attachment_emoji_filename(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -576,6 +592,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_attachment_password_in_subject(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -606,6 +623,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') + @unittest.skip() def test_email_extract_html_body_urls(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, From 1e1b18fe123ccc08d6625df6f54b1f18f3464c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 17:15:22 +0100 Subject: [PATCH 037/220] chg: Install faup in travis --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6a81c61..9d6c654 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,16 @@ install: - sudo apt-get install libzbar0 libzbar-dev libpoppler-cpp-dev tesseract-ocr - pip install pipenv - pipenv install --dev + # install pyfaup + - git clone https://github.com/stricaud/faup.git + - pushd faup/build + - cmake .. && make + - sudo make install + - popd + - ldconfig + - cd faup/src/lib/bindings/python + - pip install . + - popd script: - pipenv run coverage run -m --parallel-mode --source=misp_modules misp_modules.__init__ -l 127.0.0.1 & From 5d415bb8f2d2543fd869a53feda185c6b93bb592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 17:24:19 +0100 Subject: [PATCH 038/220] fix: Missing sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9d6c654..e2c2861 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ install: - cmake .. && make - sudo make install - popd - - ldconfig + - sudo ldconfig - cd faup/src/lib/bindings/python - pip install . - popd From 5b1ac3dc516d29249bcf8a035f9d06708c91e3b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 17:34:34 +0100 Subject: [PATCH 039/220] fix: missing pushd --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e2c2861..3fc08dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ install: - sudo make install - popd - sudo ldconfig - - cd faup/src/lib/bindings/python + - pushd faup/src/lib/bindings/python - pip install . - popd From 6fcd9c9b8d32baa4e6ae1cf0a2043bc212474aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 4 Dec 2019 17:46:09 +0100 Subject: [PATCH 040/220] fix: MIssing parameter in skip --- tests/test.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/test.py b/tests/test.py index c72d236..37abcc3 100644 --- a/tests/test.py +++ b/tests/test.py @@ -57,7 +57,7 @@ class TestModules(unittest.TestCase): assert("mrxcls.sys" in values) assert("mdmcpq3.PNF" in values) - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_headers(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -106,7 +106,7 @@ class TestModules(unittest.TestCase): self.assertEqual(types['email-reply-to'], 1) self.assertIn("", values) - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_basic(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -131,7 +131,7 @@ class TestModules(unittest.TestCase): attch_data = base64.b64decode(i["data"]) self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_unpack(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -163,7 +163,7 @@ class TestModules(unittest.TestCase): b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_dont_unpack_compressed_doc_attachments(self): """Ensures that compressed """ @@ -197,7 +197,7 @@ class TestModules(unittest.TestCase): self.assertEqual(filesum.hexdigest(), '098da5381a90d4a51e6b844c18a0fecf2e364813c2f8b317cfdc51c21f2506a5') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_unpack_with_password(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -226,7 +226,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_password_in_body(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -250,7 +250,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_password_in_body_quotes(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -279,7 +279,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_password_in_html_body(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -320,7 +320,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_body_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -341,7 +341,7 @@ class TestModules(unittest.TestCase): self.assertIn('results', response, "No server results found.") - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_header_proper_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -406,7 +406,7 @@ class TestModules(unittest.TestCase): self.assertIn("", values) - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_header_malformed_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -474,7 +474,7 @@ class TestModules(unittest.TestCase): self.assertIn("", values) - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_header_CJK_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -502,7 +502,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(japanese_charset, i['values'], "Subject not properly decoded") - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_malformed_header_CJK_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -533,7 +533,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(japanese_charset, i['values'], "Subject not properly decoded") - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_malformed_header_emoji_encoding(self): query = {"module":"email_import"} query["config"] = {"unzip_attachments": None, @@ -564,7 +564,7 @@ class TestModules(unittest.TestCase): self.assertNotEqual(RFC_format, i['values'], RFC_encoding_error) self.assertEqual(emoji_string, i['values'], "Subject not properly decoded") - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_emoji_filename(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, @@ -592,7 +592,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_attachment_password_in_subject(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": "true", @@ -623,7 +623,7 @@ class TestModules(unittest.TestCase): self.assertEqual(attch_data, 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-') - @unittest.skip() + @unittest.skip("Need Rewrite") def test_email_extract_html_body_urls(self): query = {"module": "email_import"} query["config"] = {"unzip_attachments": None, From b70c32af7bc0400154c1c9951419aea918baeb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Thu, 5 Dec 2019 19:11:01 +0100 Subject: [PATCH 041/220] fix: Somewhat broken emails needed some love --- .../modules/import_mod/email_import.py | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/misp_modules/modules/import_mod/email_import.py b/misp_modules/modules/import_mod/email_import.py index 0ffb59b..114f8c9 100644 --- a/misp_modules/modules/import_mod/email_import.py +++ b/misp_modules/modules/import_mod/email_import.py @@ -111,19 +111,20 @@ def handler(q=False): mail_body = email_object.email.get_body(preferencelist=('html', 'plain')) if extract_urls: - charset = mail_body.get_content_charset() - if mail_body.get_content_type() == 'text/html': - url_parser = HTMLURLParser() - url_parser.feed(mail_body.get_payload(decode=True).decode(charset, errors='ignore')) - urls = url_parser.urls - else: - urls = re.findall(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', mail_body.get_payload(decode=True).decode(charset, errors='ignore')) - for url in urls: - if not url: - continue - url_object = URLObject(url, standalone=False) - file_objects.append(url_object) - email_object.add_reference(url_object.uuid, 'includes', 'URL in email body') + if mail_body: + charset = mail_body.get_content_charset() + if mail_body.get_content_type() == 'text/html': + url_parser = HTMLURLParser() + url_parser.feed(mail_body.get_payload(decode=True).decode(charset, errors='ignore')) + urls = url_parser.urls + else: + urls = re.findall(r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', mail_body.get_payload(decode=True).decode(charset, errors='ignore')) + for url in urls: + if not url: + continue + url_object = URLObject(url, standalone=False) + file_objects.append(url_object) + email_object.add_reference(url_object.uuid, 'includes', 'URL in email body') objects = [email_object.to_json()] if file_objects: @@ -213,18 +214,23 @@ def get_zip_passwords(message): body = [] for part in message.walk(): charset = part.get_content_charset() + if not charset: + charset = "utf-8" if part.get_content_type() == 'text/plain': body.append(part.get_payload(decode=True).decode(charset, errors='ignore')) elif part.get_content_type() == 'text/html': html_parser = HTMLTextParser() - html_parser.feed(part.get_payload(decode=True).decode(charset, errors='ignore')) - for text in html_parser.text_data: - body.append(text) + payload = part.get_payload(decode=True) + if payload: + html_parser.feed(payload.decode(charset, errors='ignore')) + for text in html_parser.text_data: + body.append(text) raw_text = "\n".join(body).strip() # Add subject to text corpus to parse - subject = " " + message.get('Subject') - raw_text += subject + if "Subject" in message: + subject = " " + message.get('Subject') + raw_text += subject # Grab any strings that are marked off by special chars marking_chars = [["\'", "\'"], ['"', '"'], ['[', ']'], ['(', ')']] From e880191b10fcc273b056a2a6d8aaaedb48ee25fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Sun, 8 Dec 2019 19:39:44 +0100 Subject: [PATCH 042/220] chg: Bump dependencies --- Pipfile.lock | 78 +++++++++++++++++------------------ REQUIREMENTS | 113 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 106 insertions(+), 85 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6eca5ba..15a5d51 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -178,10 +178,10 @@ }, "colorama": { "hashes": [ - "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", - "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], - "version": "==0.4.1" + "version": "==0.4.3" }, "cryptography": { "hashes": [ @@ -296,11 +296,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:3a8b2dfd0a2c6a3636e7c016a7e54ae04b997d30e69d5eacdca7a6c2221a1402", + "sha256:41e688146d000891f32b1669e8573c57e39e5060e7f5f647aa617cd9a9568278" ], "markers": "python_version < '3.8'", - "version": "==1.1.0" + "version": "==1.2.0" }, "isodate": { "hashes": [ @@ -375,10 +375,10 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "multidict": { "hashes": [ @@ -708,7 +708,7 @@ "pymisp": { "editable": true, "git": "https://github.com/MISP/PyMISP.git", - "ref": "c03b26a18c83b47d2a04e908fe6c78176ec45509" + "ref": "36cc79ffb686430e02b382dfef85c29a5e27c39d" }, "pyonyphe": { "editable": true, @@ -900,10 +900,10 @@ }, "sigmatools": { "hashes": [ - "sha256:f3ffb4ad034c68c30299d2082490ffdbde9fdc1e8aa7fda26fd22a8679d2a226" + "sha256:2331bc1c6bd8e69ff3e201e51552328794f6cfc3597004fa0865341748750737" ], "index": "pypi", - "version": "==0.14" + "version": "==0.15.0" }, "six": { "hashes": [ @@ -1003,11 +1003,11 @@ }, "wand": { "hashes": [ - "sha256:13a96818a2f89e7684704ba3bfc20bdb21a15e08736c3fdf74035eeaeefd7873", - "sha256:8cfa30a71a3c65efd1d90678790297fec287300715ebcdf17e119fe075148dd0" + "sha256:46a1eb1ec092d5954d0f5e88ee216e87d9e8b7d28d36a21c342a5b13ebb6604e", + "sha256:6d0925190a846e28412814ea50fa8b3d7969859bac8a93ebc5b2f1c0a1a34d6a" ], "index": "pypi", - "version": "==0.5.7" + "version": "==0.5.8" }, "websocket-client": { "hashes": [ @@ -1056,25 +1056,25 @@ }, "yarl": { "hashes": [ - "sha256:031e8f56cf085d3b3df6b6bce756369ea7052b82d35ea07b6045f209c819e0e5", - "sha256:074958fe4578ef3a3d0bdaf96bbc25e4c4db82b7ff523594776fcf3d3f16c531", - "sha256:2db667ee21f620b446a54a793e467714fc5a446fcc82d93a47e8bde01d69afab", - "sha256:326f2dbaaa17b858ae86f261ae73a266fd820a561fc5142cee9d0fc58448fbd7", - "sha256:32a3885f542f74d0f4f87057050c6b45529ebd79d0639f56582e741521575bfe", - "sha256:56126ef061b913c3eefecace3404ca88917265d0550b8e32bbbeab29e5c830bf", - "sha256:589ac1e82add13fbdedc04eb0a83400db728e5f1af2bd273392088ca90de7062", - "sha256:6076bce2ecc6ebf6c92919d77762f80f4c9c6ecc9c1fbaa16567ec59ad7d6f1d", - "sha256:63be649c535d18ab6230efbc06a07f7779cd4336a687672defe70c025349a47b", - "sha256:6642cbc92eaffa586180f669adc772f5c34977e9e849e93f33dc142351e98c9c", - "sha256:6fa05a25f2280e78a514041d4609d39962e7d51525f2439db9ad7a2ae7aac163", - "sha256:7ed006a220422c33ff0889288be24db56ff0a3008ffe9eaead58a690715ad09b", - "sha256:80c9c213803b50899460cc355f47e66778c3c868f448b7b7de5b1f1858c82c2a", - "sha256:8bae18e2129850e76969b57869dacc72a66cccdbeebce1a28d7f3d439c21a7a3", - "sha256:ab112fba996a8f48f427e26969f2066d50080df0c24007a8cc6d7ae865e19013", - "sha256:b1c178ef813940c9a5cbad42ab7b8b76ac08b594b0a6bad91063c968e0466efc", - "sha256:d6eff151c3b23a56a5e4f496805619bc3bdf4f749f63a7a95ad50e8267c17475" + "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce", + "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6", + "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce", + "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae", + "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d", + "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f", + "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b", + "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b", + "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb", + "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462", + "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea", + "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70", + "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1", + "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a", + "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b", + "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080", + "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" ], - "version": "==1.4.1" + "version": "==1.4.2" }, "zipp": { "hashes": [ @@ -1175,11 +1175,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:3a8b2dfd0a2c6a3636e7c016a7e54ae04b997d30e69d5eacdca7a6c2221a1402", + "sha256:41e688146d000891f32b1669e8573c57e39e5060e7f5f647aa617cd9a9568278" ], "markers": "python_version < '3.8'", - "version": "==1.1.0" + "version": "==1.2.0" }, "mccabe": { "hashes": [ @@ -1190,10 +1190,10 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "nose": { "hashes": [ diff --git a/REQUIREMENTS b/REQUIREMENTS index 65c0921..b279b59 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -1,84 +1,105 @@ -i https://pypi.org/simple -e . --e git+https://github.com/D4-project/BGP-Ranking.git/@429cea9c0787876820984a2df4e982449a84c10e#egg=pybgpranking&subdirectory=client --e git+https://github.com/D4-project/IPASN-History.git/@47cd0f2658ab172fce42126ff3a1dbcddfb0b5fb#egg=pyipasnhistory&subdirectory=client +-e git+https://github.com/D4-project/BGP-Ranking.git/@fd9c0e03af9b61d4bf0b67ac73c7208a55178a54#egg=pybgpranking&subdirectory=client +-e git+https://github.com/D4-project/IPASN-History.git/@fc5e48608afc113e101ca6421bf693b7b9753f9e#egg=pyipasnhistory&subdirectory=client -e git+https://github.com/MISP/PyIntel471.git@0df8d51f1c1425de66714b3a5a45edb69b8cc2fc#egg=pyintel471 --e git+https://github.com/MISP/PyMISP.git@3e8c36dc2f34b5d812a6b6d1bd1a619f01286657#egg=pymisp +-e git+https://github.com/MISP/PyMISP.git@36cc79ffb686430e02b382dfef85c29a5e27c39d#egg=pymisp -e git+https://github.com/Rafiot/uwhoisd.git@411572840eba4c72dc321c549b36a54ed5cea9de#egg=uwhois&subdirectory=client -e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader -e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails -e git+https://github.com/sebdraven/pyonyphe@cbb0168d5cb28a9f71f7ab3773164a7039ccdb12#egg=pyonyphe aiohttp==3.4.4 -apiosintDS==1.8.1 antlr4-python3-runtime==4.7.2 ; python_version >= '3' -assemblyline_client +apiosintds==1.8.3 +argparse==1.4.0 +assemblyline-client==3.7.3 async-timeout==3.0.1 -attrs==19.1.0 +attrs==19.3.0 backscatter==0.2.4 -beautifulsoup4==4.7.1 +beautifulsoup4==4.8.1 blockchain==1.4.4 -certifi==2019.3.9 +certifi==2019.11.28 +cffi==1.13.2 chardet==3.0.4 click-plugins==1.1.1 click==7.0 -colorama==0.4.1 +colorama==0.4.3 +cryptography==2.8 +decorator==4.4.1 +deprecated==1.2.7 dnspython==1.16.0 domaintools-api==0.3.3 -enum-compat==0.0.2 +enum-compat==0.0.3 ez-setup==0.9 ezodf==0.3.2 -future==0.17.1 +future==0.18.2 geoip2==2.9.0 -httplib2==0.12.3 +httplib2==0.14.0 idna-ssl==1.1.0 ; python_version < '3.7' idna==2.8 +importlib-metadata==1.2.0 ; python_version < '3.8' isodate==0.6.0 -jbxapi==3.1.3 -jsonschema==3.0.1 -lxml==4.3.3 +jbxapi==3.4.0 +jsonschema==3.2.0 +lxml==4.4.2 maclookup==1.0.3 -multidict==4.5.2 +maxminddb==1.5.1 +more-itertools==8.0.2 +multidict==4.6.1 np==1.0.2 -numpy==1.16.3 +numpy==1.17.4 oauth2==1.9.0.post1 -opencv-python==4.1.0.25 -pandas-ods-reader==0.0.6 -pandas==0.24.2 -passivetotal==1.0.30 -pdftotext==2.1.1 -pillow==6.0.0 -psutil==5.6.2 +opencv-python==4.1.2.30 +pandas-ods-reader==0.0.7 +pandas==0.25.3 +passivetotal==1.0.31 +pdftotext==2.1.2 +pillow==6.2.1 +progressbar2==3.47.0 +psutil==5.6.7 +pycparser==2.19 +pycryptodome==3.9.4 +pycryptodomex==3.9.4 pyeupi==1.0 -pyparsing==2.4.0 +pygeoip==0.3.2 +pyopenssl==19.1.0 +pyparsing==2.4.5 pypdns==1.4.1 pypssl==2.1 -pyrsistent==0.15.2 -pytesseract==0.2.6 -python-dateutil==2.8.0 +pyrsistent==0.15.6 +pytesseract==0.3.0 +python-dateutil==2.8.1 python-docx==0.8.10 python-pptx==0.6.18 -pytz==2019.1 -pyyaml==5.1 +python-utils==2.3.0 +pytz==2019.3 +pyyaml==5.2 pyzbar==0.1.8 +pyzipper==0.3.1 ; python_version >= '3.5' rdflib==4.2.2 -redis==3.2.1 -reportlab==3.5.21 -requests-cache==0.5.0 -requests==2.22.0 -shodan==1.13.0 -sigmatools==0.10 -six==1.12.0 -soupsieve==1.9.1 +redis==3.3.11 +reportlab==3.5.32 +requests-cache==0.5.2 +requests[security]==2.22.0 +shodan==1.20.0 +sigmatools==0.15.0 +six==1.13.0 +socketio-client==0.5.6 +soupsieve==1.9.5 sparqlwrapper==1.8.4 -stix2-patterns==1.1.0 -tabulate==0.8.3 -tornado==6.0.2 +stix2-patterns==1.2.1 +tabulate==0.8.6 +tornado==6.0.3 url-normalize==1.4.1 urlarchiver==0.2 -urllib3==1.25.3 -vulners==1.5.0 -wand==0.5.3 +urllib3==1.25.7 +validators==0.14.0 +vulners==1.5.4 +wand==0.5.8 +websocket-client==0.56.0 +wrapt==1.11.2 xlrd==1.2.0 -xlsxwriter==1.1.8 +xlsxwriter==1.2.6 yara-python==3.8.1 -yarl==1.3.0 +yarl==1.4.2 +zipp==0.6.0 From 772822a903de8f7a5866075d62445d514b12d531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 10 Dec 2019 11:28:01 +0100 Subject: [PATCH 043/220] fix: OTX tests were failing, new entry. --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index d9ce6f1..4b28bd1 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -255,7 +255,7 @@ class TestExpansions(unittest.TestCase): def test_otx(self): query_types = ('domain', 'ip-src', 'md5') query_values = ('circl.lu', '8.8.8.8', '616eff3e9a7575ae73821b4668d2801c') - results = (('149.13.33.14', '149.13.33.17'), + results = (('149.13.33.14', '149.13.33.17', '6f9814ba70e68c3bce16d253e8d8f86e04a21a2b4172a0f7631040096ba2c47a'), 'ffc2595aefa80b61621023252b5f0ccb22b6e31d7f1640913cd8ff74ddbd8b41', '8.8.8.8') for query_type, query_value, result in zip(query_types, query_values, results): From e063c2a283443b02fd1a50fba6f4d3df7b63f1f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 17 Dec 2019 10:06:50 +0100 Subject: [PATCH 044/220] fix: Properly install pymisp with file object dependencies --- Pipfile | 2 +- Pipfile.lock | 165 +++++++++++++++++++++++++++++++-------------------- REQUIREMENTS | 11 ++-- 3 files changed, 108 insertions(+), 70 deletions(-) diff --git a/Pipfile b/Pipfile index 1cb0889..1a99c42 100644 --- a/Pipfile +++ b/Pipfile @@ -18,7 +18,7 @@ pypdns = "*" pypssl = "*" pyeupi = "*" uwhois = {editable = true,git = "https://github.com/Rafiot/uwhoisd.git",ref = "testing",subdirectory = "client"} -pymisp = {editable = true,git = "https://github.com/MISP/PyMISP.git"} +pymisp = {editable = true,extras = ["fileobjects,openioc,virustotal,pdfexport"],git = "https://github.com/MISP/PyMISP.git"} pyonyphe = {editable = true,git = "https://github.com/sebdraven/pyonyphe"} pydnstrails = {editable = true,git = "https://github.com/sebdraven/pydnstrails"} pytesseract = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 15a5d51..dab4860 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2cd074bb42f3fbefc9eefdcd673817af96b25fdf8e7e7a149878b7ae8bbfcc66" + "sha256": "30e84f4986146c248e706f52f425649660225889bfcdf5075c99854442ae5f42" }, "pipfile-spec": 6, "requires": { @@ -296,11 +296,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:3a8b2dfd0a2c6a3636e7c016a7e54ae04b997d30e69d5eacdca7a6c2221a1402", - "sha256:41e688146d000891f32b1669e8573c57e39e5060e7f5f647aa617cd9a9568278" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], "markers": "python_version < '3.8'", - "version": "==1.2.0" + "version": "==1.3.0" }, "isodate": { "hashes": [ @@ -323,6 +323,25 @@ ], "version": "==3.2.0" }, + "lief": { + "hashes": [ + "sha256:276cc63ec12a21bdf01b8d30962692c17499788234f0765247ca7a35872097ec", + "sha256:3e6baaeb52bdc339b5f19688b58fd8d5778b92e50221f920cedfa2bec1f4d5c2", + "sha256:45e5c592b57168c447698381d927eb2386ffdd52afe0c48245f848d4cc7ee05a", + "sha256:6547752b5db105cd41c9fa65d0d7452a4d7541b77ffee716b46246c6d81e172f", + "sha256:83b51e01627b5982662f9550ac1230758aa56945ed86829e4291932d98417da3", + "sha256:895599194ea7495bf304e39317b04df20cccf799fc2751867cc1aa4997cfcdae", + "sha256:8a91cee2568306fe1d2bf84341b459c85368317d01d7105fa49e4f4ede837076", + "sha256:913b36a67707dc2afa72f117bab9856ea3f434f332b04a002a0f9723c8779320", + "sha256:9f604a361a3b1b3ed5fdafed0321c5956cb3b265b5efe2250d1bf8911a80c65b", + "sha256:a487fe7234c04bccd58223dbb79214421176e2629814c7a4a887764cceb5be7c", + "sha256:bc8488fb0661cb436fe4bb4fe947d0f9aa020e9acaed233ccf01ab04d888c68a", + "sha256:bddbf333af62310a10cb738a1df1dc2b140dd9c663b55ba3500c10c249d416d2", + "sha256:cce48d7c97cef85e01e6cfeff55f2068956b5c0257eb9c2d2c6d15e33dd1e4fc", + "sha256:f8b3f66956c56b582b3adc573bf2a938c25fb21c8894b373a113e24c494fc982" + ], + "version": "==0.10.1" + }, "lxml": { "hashes": [ "sha256:00ac0d64949fef6b3693813fe636a2d56d97a5a49b5bbb86e4cc4cc50ebc9ea2", @@ -382,25 +401,25 @@ }, "multidict": { "hashes": [ - "sha256:07f9a6bf75ad675d53956b2c6a2d4ef2fa63132f33ecc99e9c24cf93beb0d10b", - "sha256:0ffe4d4d28cbe9801952bfb52a8095dd9ffecebd93f84bdf973c76300de783c5", - "sha256:1b605272c558e4c659dbaf0fb32a53bfede44121bcf77b356e6e906867b958b7", - "sha256:205a011e636d885af6dd0029e41e3514a46e05bb2a43251a619a6e8348b96fc0", - "sha256:250632316295f2311e1ed43e6b26a63b0216b866b45c11441886ac1543ca96e1", - "sha256:2bc9c2579312c68a3552ee816311c8da76412e6f6a9cf33b15152e385a572d2a", - "sha256:318aadf1cfb6741c555c7dd83d94f746dc95989f4f106b25b8a83dfb547f2756", - "sha256:42cdd649741a14b0602bf15985cad0dd4696a380081a3319cd1ead46fd0f0fab", - "sha256:5159c4975931a1a78bf6602bbebaa366747fce0a56cb2111f44789d2c45e379f", - "sha256:87e26d8b89127c25659e962c61a4c655ec7445d19150daea0759516884ecb8b4", - "sha256:891b7e142885e17a894d9d22b0349b92bb2da4769b4e675665d0331c08719be5", - "sha256:8d919034420378132d074bf89df148d0193e9780c9fe7c0e495e895b8af4d8a2", - "sha256:9c890978e2b37dd0dc1bd952da9a5d9f245d4807bee33e3517e4119c48d66f8c", - "sha256:a37433ce8cdb35fc9e6e47e1606fa1bfd6d70440879038dca7d8dd023197eaa9", - "sha256:c626029841ada34c030b94a00c573a0c7575fe66489cde148785b6535397d675", - "sha256:cfec9d001a83dc73580143f3c77e898cf7ad78b27bb5e64dbe9652668fcafec7", - "sha256:efaf1b18ea6c1f577b1371c0159edbe4749558bfe983e13aa24d0a0c01e1ad7b" + "sha256:09c19f642e055550c9319d5123221b7e07fc79bda58122aa93910e52f2ab2f29", + "sha256:0c1a5d5f7aa7189f7b83c4411c2af8f1d38d69c4360d5de3eea129c65d8d7ce2", + "sha256:12f22980e7ed0972a969520fb1e55682c9fca89a68b21b49ec43132e680be812", + "sha256:258660e9d6b52de1a75097944e12718d3aa59adc611b703361e3577d69167aaf", + "sha256:3374a23e707848f27b3438500db0c69eca82929337656fce556bd70031fbda74", + "sha256:503b7fce0054c73aa631cc910a470052df33d599f3401f3b77e54d31182525d5", + "sha256:6ce55f2c45ffc90239aab625bb1b4864eef33f73ea88487ef968291fbf09fb3f", + "sha256:725496dde5730f4ad0a627e1a58e2620c1bde0ad1c8080aae15d583eb23344ce", + "sha256:a3721078beff247d0cd4fb19d915c2c25f90907cf8d6cd49d0413a24915577c6", + "sha256:ba566518550f81daca649eded8b5c7dd09210a854637c82351410aa15c49324a", + "sha256:c42362750a51a15dc905cb891658f822ee5021bfbea898c03aa1ed833e2248a5", + "sha256:cf14aaf2ab067ca10bca0b14d5cbd751dd249e65d371734bc0e47ddd8fafc175", + "sha256:cf24e15986762f0e75a622eb19cfe39a042e952b8afba3e7408835b9af2be4fb", + "sha256:d7b6da08538302c5245cd3103f333655ba7f274915f1f5121c4f4b5fbdb3febe", + "sha256:e27e13b9ff0a914a6b8fb7e4947d4ac6be8e4f61ede17edffabd088817df9e26", + "sha256:e53b205f8afd76fc6c942ef39e8ee7c519c775d336291d32874082a87802c67c", + "sha256:ec804fc5f68695d91c24d716020278fcffd50890492690a7e1fef2e741f7172c" ], - "version": "==4.6.1" + "version": "==4.7.1" }, "np": { "hashes": [ @@ -674,6 +693,12 @@ ], "version": "==3.9.4" }, + "pydeep": { + "hashes": [ + "sha256:22866eb422d1d5907f8076ee792da65caecb172425d27576274e2a8eacf6afc1" + ], + "version": "==0.4" + }, "pydnstrails": { "editable": true, "git": "https://github.com/sebdraven/pydnstrails", @@ -707,8 +732,11 @@ }, "pymisp": { "editable": true, + "extras": [ + "fileobjects,openioc,virustotal,pdfexport" + ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "36cc79ffb686430e02b382dfef85c29a5e27c39d" + "ref": "a26a8e450b14d48bb0c8ef46b32bff2f1eadc514" }, "pyonyphe": { "editable": true, @@ -771,6 +799,13 @@ "index": "pypi", "version": "==0.8.10" }, + "python-magic": { + "hashes": [ + "sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375", + "sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5" + ], + "version": "==0.4.15" + }, "python-pptx": { "hashes": [ "sha256:a857d69e52d7e8a8fb32fca8182fdd4a3c68c689de8d4e4460e9b4a95efa7bc4" @@ -893,14 +928,15 @@ }, "shodan": { "hashes": [ - "sha256:2efe383eeb083eb67137a00cc6fc5ea1fd848ce8053dfdea6696bc6ec05f6e98" + "sha256:eab999bca9d3b30e6fc549e609194ff2d6fac3caea252414e1d8d735efab8342" ], "index": "pypi", - "version": "==1.20.0" + "version": "==1.21.0" }, "sigmatools": { "hashes": [ - "sha256:2331bc1c6bd8e69ff3e201e51552328794f6cfc3597004fa0865341748750737" + "sha256:2331bc1c6bd8e69ff3e201e51552328794f6cfc3597004fa0865341748750737", + "sha256:4361515fb8d6c6389cc0d1e5057b1f7d4cec11b8fb814e561253c01050efa634" ], "index": "pypi", "version": "==0.15.0" @@ -1116,40 +1152,39 @@ }, "coverage": { "hashes": [ - "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", - "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", - "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", - "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", - "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", - "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", - "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", - "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", - "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", - "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", - "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", - "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", - "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", - "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", - "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", - "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", - "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", - "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", - "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", - "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", - "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", - "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", - "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", - "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", - "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", - "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", - "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", - "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", - "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", - "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", - "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", - "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025" + "sha256:0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", + "sha256:1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", + "sha256:2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", + "sha256:3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", + "sha256:44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", + "sha256:475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", + "sha256:47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", + "sha256:4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", + "sha256:50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", + "sha256:56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", + "sha256:79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", + "sha256:79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", + "sha256:7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", + "sha256:81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", + "sha256:81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", + "sha256:8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", + "sha256:88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", + "sha256:91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", + "sha256:979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", + "sha256:9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", + "sha256:a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", + "sha256:b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", + "sha256:ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", + "sha256:be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", + "sha256:c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", + "sha256:c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", + "sha256:d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", + "sha256:d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", + "sha256:e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", + "sha256:e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", + "sha256:fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c" ], - "version": "==4.5.4" + "version": "==5.0" }, "entrypoints": { "hashes": [ @@ -1175,11 +1210,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:3a8b2dfd0a2c6a3636e7c016a7e54ae04b997d30e69d5eacdca7a6c2221a1402", - "sha256:41e688146d000891f32b1669e8573c57e39e5060e7f5f647aa617cd9a9568278" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], "markers": "python_version < '3.8'", - "version": "==1.2.0" + "version": "==1.3.0" }, "mccabe": { "hashes": [ @@ -1248,11 +1283,11 @@ }, "pytest": { "hashes": [ - "sha256:63344a2e3bce2e4d522fd62b4fdebb647c019f1f9e4ca075debbd13219db4418", - "sha256:f67403f33b2b1d25a6756184077394167fe5e2f9d8bdaab30707d19ccec35427" + "sha256:6b571215b5a790f9b41f19f3531c53a45cf6bb8ef2988bc1ff9afb38270b25fa", + "sha256:e41d489ff43948babd0fad7ad5e49b8735d5d55e26628a58673c39ff61d95de4" ], "index": "pypi", - "version": "==5.3.1" + "version": "==5.3.2" }, "requests": { "extras": [ diff --git a/REQUIREMENTS b/REQUIREMENTS index b279b59..ee6c7c1 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -3,7 +3,7 @@ -e git+https://github.com/D4-project/BGP-Ranking.git/@fd9c0e03af9b61d4bf0b67ac73c7208a55178a54#egg=pybgpranking&subdirectory=client -e git+https://github.com/D4-project/IPASN-History.git/@fc5e48608afc113e101ca6421bf693b7b9753f9e#egg=pyipasnhistory&subdirectory=client -e git+https://github.com/MISP/PyIntel471.git@0df8d51f1c1425de66714b3a5a45edb69b8cc2fc#egg=pyintel471 --e git+https://github.com/MISP/PyMISP.git@36cc79ffb686430e02b382dfef85c29a5e27c39d#egg=pymisp +-e git+https://github.com/MISP/PyMISP.git@a26a8e450b14d48bb0c8ef46b32bff2f1eadc514#egg=pymisp[fileobjects,openioc,virustotal,pdfexport] -e git+https://github.com/Rafiot/uwhoisd.git@411572840eba4c72dc321c549b36a54ed5cea9de#egg=uwhois&subdirectory=client -e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader -e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails @@ -37,15 +37,16 @@ geoip2==2.9.0 httplib2==0.14.0 idna-ssl==1.1.0 ; python_version < '3.7' idna==2.8 -importlib-metadata==1.2.0 ; python_version < '3.8' +importlib-metadata==1.3.0 ; python_version < '3.8' isodate==0.6.0 jbxapi==3.4.0 jsonschema==3.2.0 +lief==0.10.1 lxml==4.4.2 maclookup==1.0.3 maxminddb==1.5.1 more-itertools==8.0.2 -multidict==4.6.1 +multidict==4.7.1 np==1.0.2 numpy==1.17.4 oauth2==1.9.0.post1 @@ -60,6 +61,7 @@ psutil==5.6.7 pycparser==2.19 pycryptodome==3.9.4 pycryptodomex==3.9.4 +pydeep==0.4 pyeupi==1.0 pygeoip==0.3.2 pyopenssl==19.1.0 @@ -70,6 +72,7 @@ pyrsistent==0.15.6 pytesseract==0.3.0 python-dateutil==2.8.1 python-docx==0.8.10 +python-magic==0.4.15 python-pptx==0.6.18 python-utils==2.3.0 pytz==2019.3 @@ -81,7 +84,7 @@ redis==3.3.11 reportlab==3.5.32 requests-cache==0.5.2 requests[security]==2.22.0 -shodan==1.20.0 +shodan==1.21.0 sigmatools==0.15.0 six==1.13.0 socketio-client==0.5.6 From adda243c33567cf7aefb876671366d4a705501da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Tue, 17 Dec 2019 10:19:05 +0100 Subject: [PATCH 045/220] fix: Missing dependency in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3fc08dc..0b87679 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 tesseract-ocr + - sudo apt-get install libzbar0 libzbar-dev libpoppler-cpp-dev tesseract-ocr libfuzzy-dev - pip install pipenv - pipenv install --dev # install pyfaup From 6849daebfabb97fc75a75676bdbe5071262ba1ef Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 10:26:43 +0100 Subject: [PATCH 046/220] chg: Made circl_passivessl module able to return MISP objects --- .../modules/expansion/circl_passivessl.py | 96 ++++++++++++++----- 1 file changed, 74 insertions(+), 22 deletions(-) diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index c6d5a3f..a40d41f 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -1,35 +1,87 @@ import json import pypssl +from pymisp import MISPAttribute, MISPEvent, MISPObject -misperrors = {'error': 'Error'} -mispattributes = {'input': ['ip-src', 'ip-dst'], 'output': ['freetext']} -moduleinfo = {'version': '0.1', 'author': 'Raphaël Vinot', 'description': 'Module to access CIRCL Passive SSL', 'module-type': ['expansion', 'hover']} +mispattributes = {'input': ['ip-src', 'ip-dst'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.2', 'author': 'Raphaël Vinot', + 'description': 'Module to access CIRCL Passive SSL', + 'module-type': ['expansion', 'hover']} moduleconfig = ['username', 'password'] +class PassiveSSLParser(): + def __init__(self, attribute, authentication): + self.misp_event = MISPEvent() + self.attribute = MISPAttribute() + self.attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.attribute) + self.pssl = pypssl.PyPSSL(basic_auth=authentication) + self.cert_hash = 'x509-fingerprint-sha1' + self.cert_type = 'pem' + self.mapping = {'issuer': ('text', 'issuer'), + 'keylength': ('text', 'pubkey-info-size'), + 'not_after': ('datetime', 'validity-not-after'), + 'not_before': ('datetime', 'validity-not-before'), + 'subject': ('text', 'subject')} + + def get_results(self): + if hasattr(self, 'result'): + return self.results + event = json.loads(self.misp_event.to_json()) + results = {key:event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + def parse(self, value): + try: + results = self.pssl.query(self.attribute.value) + except Exception: + self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} + return + cert_hash = 'x509-fingerprint-sha1' + cert_type = 'pem' + for ip_address, certificates in results.items(): + ip_uuid = self._handle_ip_attribute(ip_address) + for certificate in certificates['certificates']: + self._handle_certificate(certificate, ip_uuid) + + def _handle_certificate(self, certificate, ip_uuid): + x509 = MISPObject('x509') + x509.add_attribute(self.cert_hash, type=self.cert_hash, value = certificate) + cert_details = self.pssl.fetch_cert(certificate) + info = cert_details['info'] + for feature, mapping in self.mapping.items(): + attribute_type, object_relation = mapping + x509.add_attribute(object_relation, type=attribute_type, value=info[feature]) + x509.add_attribute(self.cert_type, type='text', value=self.cert_type) + x509.add_reference(ip_uuid, 'seen-by') + self.misp_event.add_object(**x509) + + def _handle_ip_attribute(self, ip_address): + if ip_address == self.attribute.value: + return self.attribute.uuid + ip_attribute = MISPAttribute() + ip_attribute.from_dict(**{'type': self.attribute.type, 'value': ip_address}) + self.misp_event.add_attribute(**ip_attribute) + return ip_attribute.uuid + + def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('ip-src'): - toquery = request['ip-src'] - elif request.get('ip-dst'): - toquery = request['ip-dst'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors - - if request.get('config'): - if (request['config'].get('username') is None) or (request['config'].get('password') is None): - misperrors['error'] = 'CIRCL Passive SSL authentication is missing' - return misperrors - - x = pypssl.PyPSSL(basic_auth=(request['config']['username'], request['config']['password'])) - res = x.query(toquery) - out = res.get(toquery) - - r = {'results': [{'types': mispattributes['output'], 'values': out}]} - return r + if not request.get('config'): + return {'error': 'CIRCL Passive SSL authentication is missing.'} + if not request['config'].get('username') or not request['config'].get('password'): + return {'error': 'CIRCL Passive SSL authentication is incomplete, please provide your username and password.'} + authentication = (request['config']['username'], request['config']['password']) + if not request.get('attribute'): + return {'error': 'Unsupported input.'} + attribute = request['attribute'] + if not any(input_type == attribute['type'] for input_type in mispattributes['input']): + return {'error': 'Unsupported attributes type'} + pssl_parser = PassiveSSLParser(attribute, authentication) + pssl_parser.parse(attribute['value']) + return pssl_parser.get_results() def introspection(): From 9da6a3744c32d2bf2f7b2b06e369f541ca87136a Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 10:35:05 +0100 Subject: [PATCH 047/220] chg: Updated documentation following the latest changes on the passive ssl module --- README.md | 2 +- doc/expansion/circl_passivessl.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6f56434..38ab966 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. -* [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate seen. +* [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate(s) seen. * [countrycode](misp_modules/modules/expansion/countrycode.py) - a hover module to tell you what country a URL belongs to. * [CrowdStrike Falcon](misp_modules/modules/expansion/crowdstrike_falcon.py) - an expansion module to expand using CrowdStrike Falcon Intel Indicator API. * [CVE](misp_modules/modules/expansion/cve.py) - a hover module to give more information about a vulnerability (CVE). diff --git a/doc/expansion/circl_passivessl.json b/doc/expansion/circl_passivessl.json index ec449ee..f9792e1 100644 --- a/doc/expansion/circl_passivessl.json +++ b/doc/expansion/circl_passivessl.json @@ -2,8 +2,8 @@ "description": "Modules to access CIRCL Passive SSL.", "logo": "logos/passivessl.png", "requirements": ["pypssl: Passive SSL python library", "A CIRCL passive SSL account with username & password"], - "input": "Ip-address attribute.", - "output": "Text describing passive SSL information related to the input attribute.", - "features": "This module takes an ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive SSL REST API to get and display information about this input.\n\nTo make it work a username and a password are thus required to authenticate to the CIRCL Passive SSL API.", + "input": "IP address attribute.", + "output": "x509 certificate objects seen by the IP address(es).", + "features": "This module takes an ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive SSL REST API to gather the related certificates and return the corresponding MISP objects.\n\nTo make it work a username and a password are required to authenticate to the CIRCL Passive SSL API.", "references": ["https://www.circl.lu/services/passive-ssl/"] } From 9c9f01b6ffb7343a6bb674c46d95938a37a51bf8 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 11:17:56 +0100 Subject: [PATCH 048/220] fix: Quick variable name fix --- misp_modules/modules/expansion/circl_passivessl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index a40d41f..2e6a939 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -26,7 +26,7 @@ class PassiveSSLParser(): def get_results(self): if hasattr(self, 'result'): - return self.results + return self.result event = json.loads(self.misp_event.to_json()) results = {key:event[key] for key in ('Attribute', 'Object')} return {'results': results} From b8d6141cb77dc4cd05a2218a208974ad6761c7e3 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 11:18:21 +0100 Subject: [PATCH 049/220] chg: Made circl_passivedns module able to return MISP objects --- .../modules/expansion/circl_passivedns.py | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/misp_modules/modules/expansion/circl_passivedns.py b/misp_modules/modules/expansion/circl_passivedns.py index 263b92a..9c095c5 100755 --- a/misp_modules/modules/expansion/circl_passivedns.py +++ b/misp_modules/modules/expansion/circl_passivedns.py @@ -1,41 +1,64 @@ import json import pypdns +from pymisp import MISPAttribute, MISPEvent, MISPObject -misperrors = {'error': 'Error'} -mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst'], 'output': ['freetext']} -moduleinfo = {'version': '0.1', 'author': 'Alexandre Dulaunoy', 'description': 'Module to access CIRCL Passive DNS', 'module-type': ['expansion', 'hover']} +mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.2', 'author': 'Alexandre Dulaunoy', + 'description': 'Module to access CIRCL Passive DNS', + 'module-type': ['expansion', 'hover']} moduleconfig = ['username', 'password'] +class PassiveDNSParser(): + def __init__(self, attribute, authentication): + self.misp_event = MISPEvent() + self.attribute = MISPAttribute() + self.attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.attribute) + self.pdns = pypdns.PyPDNS(basic_auth=authentication) + + def get_results(self): + if hasattr(self, 'result'): + return self.result + event = json.loads(self.misp_event.to_json()) + results = {key:event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + def parse(self, value): + try: + results = self.pdns.query(self.attribute.value) + except Exception: + self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} + return + mapping = {'count': 'counter', 'origin': 'text', + 'time_first': 'datetime', 'rrtype': 'text', + 'rrname': 'text', 'rdata': 'text', + 'time_last': 'datetime'} + for result in results: + pdns_object = MISPObject('passive-dns') + for relation, attribute_type in mapping.items(): + pdns_object.add_attribute(relation, type=attribute_type, value=result[relation]) + pdns_object.add_reference(self.attribute.uuid, 'associated-to') + self.misp_event.add_object(**pdns_object) + + def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('hostname'): - toquery = request['hostname'] - elif request.get('domain'): - toquery = request['domain'] - elif request.get('ip-src'): - toquery = request['ip-src'] - elif request.get('ip-dst'): - toquery = request['ip-dst'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors - - if (request.get('config')): - if (request['config'].get('username') is None) or (request['config'].get('password') is None): - misperrors['error'] = 'CIRCL Passive DNS authentication is missing' - return misperrors - - x = pypdns.PyPDNS(basic_auth=(request['config']['username'], request['config']['password'])) - res = x.query(toquery) - out = '' - for v in res: - out = out + "{} ".format(v['rdata']) - - r = {'results': [{'types': mispattributes['output'], 'values': out}]} - return r + if not request.get('config'): + return {'error': 'CIRCL Passive DNS authentication is missing.'} + if not request['config'].get('username') or not request['config'].get('password'): + return {'error': 'CIRCL Passive DNS authentication is incomplete, please provide your username and password.'} + authentication = (request['config']['username'], request['config']['password']) + if not request.get('attribute'): + return {'error': 'Unsupported input.'} + attribute = request['attribute'] + if not any(input_type == attribute['type'] for input_type in mispattributes['input']): + return {'error': 'Unsupported attributes type'} + pdns_parser = PassiveDNSParser(attribute, authentication) + pdns_parser.parse(attribute['value']) + return pdns_parser.get_results() def introspection(): From fd5e9e0cf6bd117bde475c78a2636277780be397 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 11:21:39 +0100 Subject: [PATCH 050/220] chg: Updated documentation following the latest changes on the passive dns module --- doc/expansion/circl_passivedns.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/expansion/circl_passivedns.json b/doc/expansion/circl_passivedns.json index fda50eb..024437c 100644 --- a/doc/expansion/circl_passivedns.json +++ b/doc/expansion/circl_passivedns.json @@ -3,7 +3,7 @@ "logo": "logos/passivedns.png", "requirements": ["pypdns: Passive DNS python library", "A CIRCL passive DNS account with username & password"], "input": "Hostname, domain, or ip-address attribute.", - "ouput": "Text describing passive DNS information related to the input attribute.", - "features": "This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get and display information about this input.\n\nTo make it work a username and a password are thus required to authenticate to the CIRCL Passive DNS API.", + "ouput": "Passive DNS objects related to the input attribute.", + "features": "This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get the asssociated passive dns entries and return them as MISP objects.\n\nTo make it work a username and a password are thus required to authenticate to the CIRCL Passive DNS API.", "references": ["https://www.circl.lu/services/passive-dns/", "https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/"] } From 306e9f320f922b2cb072425334e9e95310b86003 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 11:22:33 +0100 Subject: [PATCH 051/220] chg: Regenerated the modules documentation following the latest changes --- doc/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/README.md b/doc/README.md index 143c716..eb5e89d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -138,13 +138,13 @@ An expansion hover module to get a blockchain balance from a BTC address in MISP Module to access CIRCL Passive DNS. - **features**: ->This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get and display information about this input. +>This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get the asssociated passive dns entries and return them as MISP objects. > >To make it work a username and a password are thus required to authenticate to the CIRCL Passive DNS API. - **input**: >Hostname, domain, or ip-address attribute. - **ouput**: ->Text describing passive DNS information related to the input attribute. +>Passive DNS objects related to the input attribute. - **references**: >https://www.circl.lu/services/passive-dns/, https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/ - **requirements**: @@ -158,13 +158,13 @@ Module to access CIRCL Passive DNS. Modules to access CIRCL Passive SSL. - **features**: ->This module takes an ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive SSL REST API to get and display information about this input. +>This module takes an ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive SSL REST API to gather the related certificates and return the corresponding MISP objects. > ->To make it work a username and a password are thus required to authenticate to the CIRCL Passive SSL API. +>To make it work a username and a password are required to authenticate to the CIRCL Passive SSL API. - **input**: ->Ip-address attribute. +>IP address attribute. - **output**: ->Text describing passive SSL information related to the input attribute. +>x509 certificate objects seen by the IP address(es). - **references**: >https://www.circl.lu/services/passive-ssl/ - **requirements**: From 5f90ae776f67b083c1dd4616ff52aa6b5557dea1 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 14:29:29 +0100 Subject: [PATCH 052/220] fix: Making pep8 happy --- misp_modules/modules/expansion/circl_passivedns.py | 2 +- misp_modules/modules/expansion/circl_passivessl.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/misp_modules/modules/expansion/circl_passivedns.py b/misp_modules/modules/expansion/circl_passivedns.py index 9c095c5..75ff6c6 100755 --- a/misp_modules/modules/expansion/circl_passivedns.py +++ b/misp_modules/modules/expansion/circl_passivedns.py @@ -21,7 +21,7 @@ class PassiveDNSParser(): if hasattr(self, 'result'): return self.result event = json.loads(self.misp_event.to_json()) - results = {key:event[key] for key in ('Attribute', 'Object')} + results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} def parse(self, value): diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index 2e6a939..d547fc6 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -37,8 +37,6 @@ class PassiveSSLParser(): except Exception: self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} return - cert_hash = 'x509-fingerprint-sha1' - cert_type = 'pem' for ip_address, certificates in results.items(): ip_uuid = self._handle_ip_attribute(ip_address) for certificate in certificates['certificates']: @@ -46,7 +44,7 @@ class PassiveSSLParser(): def _handle_certificate(self, certificate, ip_uuid): x509 = MISPObject('x509') - x509.add_attribute(self.cert_hash, type=self.cert_hash, value = certificate) + x509.add_attribute(self.cert_hash, type=self.cert_hash, value=certificate) cert_details = self.pssl.fetch_cert(certificate) info = cert_details['info'] for feature, mapping in self.mapping.items(): From 3f7ee7c1a2e4e03086b8c126d8ea42bd47354b14 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 15:19:29 +0100 Subject: [PATCH 053/220] add: Test cases for reworked passive dns and ssl modules --- tests/test_expansions.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 4b28bd1..5fab92f 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -93,6 +93,40 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), '1es14c7qlb5cyhlmuekctxlgc1fv2ti9da fraudolent bitcoin address') + def test_circl_passivedns(self): + module_name = "circl_passivedns" + query = {"module": module_name, + "attribute": {"type": "domain", + "value": "circl.lu", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}, + "config": {}} + if module_name in self.configs: + query['config'] = self.configs[module_name] + response = self.misp_modules_post(query) + try: + self.assertEqual(self.get_object(response), 'passive-dns') + except Exception: + self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) + else: + self.assertTrue(self.get_errors(response).startswith('CIRCL Passive DNS authentication is incomplete')) + + def test_circl_passivessl(self): + module_name = "circl_passivessl" + query = {"module": module_name, + "attribute": {"type": "", + "value": "", + "uuid": ""}, + "config": {}} + if module_name in self.configs: + query['config'] = self.configs[module_name] + response = self.misp_modules_post(query) + try: + self.assertEqual(self.get_object(response), 'x509') + except Exception: + self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) + else: + self.assertTrue(self.get_errors(response).startswith('CIRCL Passive SSL authentication is incomplete')) + def test_countrycode(self): query = {"module": "countrycode", "domain": "www.circl.lu"} response = self.misp_modules_post(query) From aa721acfd9dcc2d1cd46fce7309449ccc3981f7a Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 15:47:22 +0100 Subject: [PATCH 054/220] fix: [tests] Added missing variable --- tests/test_expansions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 5fab92f..a2bda45 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -108,6 +108,7 @@ class TestExpansions(unittest.TestCase): except Exception: self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) else: + response = self.misp_modules_post(query) self.assertTrue(self.get_errors(response).startswith('CIRCL Passive DNS authentication is incomplete')) def test_circl_passivessl(self): @@ -125,6 +126,7 @@ class TestExpansions(unittest.TestCase): except Exception: self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) else: + response = self.misp_modules_post(query) self.assertTrue(self.get_errors(response).startswith('CIRCL Passive SSL authentication is incomplete')) def test_countrycode(self): From 3007761a551dbea7097ec04b850a8da28e63f444 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 16:31:53 +0100 Subject: [PATCH 055/220] fix: Making pep8 happy by having spaces around '+' operators --- misp_modules/modules/expansion/apiosintds.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/misp_modules/modules/expansion/apiosintds.py b/misp_modules/modules/expansion/apiosintds.py index 011cf6e..ac0dfa4 100644 --- a/misp_modules/modules/expansion/apiosintds.py +++ b/misp_modules/modules/expansion/apiosintds.py @@ -107,7 +107,7 @@ def apiosintParser(response, import_related_hashes): for key in response: for item in response[key]["items"]: if item["response"]: - comment = item["item"]+" IS listed by OSINT.digitalside.it. Date list: "+response[key]["list"]["date"] + comment = item["item"] + " IS listed by OSINT.digitalside.it. Date list: " + response[key]["list"]["date"] if key == "url": if "hashes" in item.keys(): if "sha256" in item["hashes"].keys(): @@ -124,16 +124,16 @@ def apiosintParser(response, import_related_hashes): if import_related_hashes: if "hashes" in urls.keys(): if "sha256" in urls["hashes"].keys(): - ret.append({"types": ["sha256"], "values": [urls["hashes"]["sha256"]], "comment": "Related to: "+itemToInclude}) + ret.append({"types": ["sha256"], "values": [urls["hashes"]["sha256"]], "comment": "Related to: " + itemToInclude}) if "sha1" in urls["hashes"].keys(): - ret.append({"types": ["sha1"], "values": [urls["hashes"]["sha1"]], "comment": "Related to: "+itemToInclude}) + ret.append({"types": ["sha1"], "values": [urls["hashes"]["sha1"]], "comment": "Related to: " + itemToInclude}) if "md5" in urls["hashes"].keys(): - ret.append({"types": ["md5"], "values": [urls["hashes"]["md5"]], "comment": "Related to: "+itemToInclude}) - ret.append({"types": ["url"], "values": [itemToInclude], "comment": "Related to: "+item["item"]}) + ret.append({"types": ["md5"], "values": [urls["hashes"]["md5"]], "comment": "Related to: " + itemToInclude}) + ret.append({"types": ["url"], "values": [itemToInclude], "comment": "Related to: " + item["item"]}) else: - ret.append({"types": ["url"], "values": [urls], "comment": "Related URL to: "+item["item"]}) + ret.append({"types": ["url"], "values": [urls], "comment": "Related URL to: " + item["item"]}) else: - comment = item["item"]+" IS NOT listed by OSINT.digitalside.it. Date list: "+response[key]["list"]["date"] + comment = item["item"] + " IS NOT listed by OSINT.digitalside.it. Date list: " + response[key]["list"]["date"] ret.append({"types": ["text"], "values": [comment]}) return ret From 2fc9171a3fa1327c7da96b12cd96b832eb7f5a99 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 16:32:29 +0100 Subject: [PATCH 056/220] fix: [tests] Avoiding issues with btc addresses --- tests/test_expansions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index a2bda45..4636e4d 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -86,7 +86,10 @@ class TestExpansions(unittest.TestCase): def test_btc_steroids(self): query = {"module": "btc_steroids", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} response = self.misp_modules_post(query) - self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0000000000 BTC (+0.0005355700 BTC / -0.0005355700 BTC)')) + try: + self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0000000000 BTC (+0.0005355700 BTC / -0.0005355700 BTC)')) + except Exception: + self.assertEqual(self.get_values(response), 'Not a valid BTC address') def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From c41545debbf57da660cf30b824c55c87bba49425 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 16:46:26 +0100 Subject: [PATCH 057/220] fix: [tests] Fixed error catching in passive dns and ssl modules --- 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 4636e4d..528fb4a 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -112,7 +112,7 @@ class TestExpansions(unittest.TestCase): self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) else: response = self.misp_modules_post(query) - self.assertTrue(self.get_errors(response).startswith('CIRCL Passive DNS authentication is incomplete')) + self.assertTrue(self.get_errors(response).startswith('CIRCL Passive DNS authentication is missing.')) def test_circl_passivessl(self): module_name = "circl_passivessl" @@ -130,7 +130,7 @@ class TestExpansions(unittest.TestCase): self.assertTrue(self.get_errors(response).startswith('There is an authentication error')) else: response = self.misp_modules_post(query) - self.assertTrue(self.get_errors(response).startswith('CIRCL Passive SSL authentication is incomplete')) + self.assertTrue(self.get_errors(response).startswith('CIRCL Passive SSL authentication is missing.')) def test_countrycode(self): query = {"module": "countrycode", "domain": "www.circl.lu"} From fd711475dd84749063f9ff15961453f90c804101 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 17:00:03 +0100 Subject: [PATCH 058/220] fix: [tests] Fixed copy paste issue --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 528fb4a..28500c4 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -89,7 +89,7 @@ class TestExpansions(unittest.TestCase): try: self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0000000000 BTC (+0.0005355700 BTC / -0.0005355700 BTC)')) except Exception: - self.assertEqual(self.get_values(response), 'Not a valid BTC address') + self.assertEqual(self.get_errors(response), 'Not a valid BTC address') def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From 6a041bc3eec56931906cdf2c56fedc651a44ac9f Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Dec 2019 23:46:37 +0100 Subject: [PATCH 059/220] Revert "fix: [tests] Fixed copy paste issue" This reverts commit fd711475dd84749063f9ff15961453f90c804101. --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 28500c4..528fb4a 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -89,7 +89,7 @@ class TestExpansions(unittest.TestCase): try: self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0000000000 BTC (+0.0005355700 BTC / -0.0005355700 BTC)')) except Exception: - self.assertEqual(self.get_errors(response), 'Not a valid BTC address') + self.assertEqual(self.get_values(response), 'Not a valid BTC address') def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From 0f455408157523257edef420a44c9eb9bcca056f Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Dec 2019 14:54:56 +0100 Subject: [PATCH 060/220] fix: [tests] With values, tests are always better ... --- tests/test_expansions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 528fb4a..79aa401 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -117,9 +117,9 @@ class TestExpansions(unittest.TestCase): def test_circl_passivessl(self): module_name = "circl_passivessl" query = {"module": module_name, - "attribute": {"type": "", - "value": "", - "uuid": ""}, + "attribute": {"type": "ip-dst", + "value": "149.13.33.14", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}, "config": {}} if module_name in self.configs: query['config'] = self.configs[module_name] From 2fc0b44b9061ede5bff9599ca1a5d1b3557d232a Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Dec 2019 16:16:47 +0100 Subject: [PATCH 061/220] fix: Making pep8 happy with whitespace after ':' --- misp_modules/modules/expansion/circl_passivessl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index d547fc6..0c11106 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -28,7 +28,7 @@ class PassiveSSLParser(): if hasattr(self, 'result'): return self.result event = json.loads(self.misp_event.to_json()) - results = {key:event[key] for key in ('Attribute', 'Object')} + results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} def parse(self, value): From 7945d060ff11bfd538590fdc01329ef56bfcbeba Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Dec 2019 17:11:13 +0100 Subject: [PATCH 062/220] new: Enrichment module for querying APIVoid with domain attributes --- misp_modules/modules/expansion/__init__.py | 2 +- misp_modules/modules/expansion/apivoid.py | 90 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100755 misp_modules/modules/expansion/apivoid.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 91e7459..12c2ab6 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -14,6 +14,6 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c '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', - 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', + 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'lastline_query', 'lastline_submit'] diff --git a/misp_modules/modules/expansion/apivoid.py b/misp_modules/modules/expansion/apivoid.py new file mode 100755 index 0000000..5d6395e --- /dev/null +++ b/misp_modules/modules/expansion/apivoid.py @@ -0,0 +1,90 @@ +import json +import requests +from pymisp import MISPAttribute, MISPEvent, MISPObject + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['domain', 'hostname'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.1', 'author': 'Christian Studer', + 'description': 'On demand query API for APIVoid.', + 'module-type': ['expansion', 'hover']} +moduleconfig = ['apikey'] + + +class APIVoidParser(): + def __init__(self, attribute): + self.misp_event = MISPEvent() + self.attribute = MISPAttribute() + self.attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.attribute) + self.url = 'https://endpoint.apivoid.com/{}/v1/pay-as-you-go/?key={}&' + + def get_results(self): + if hasattr(self, 'result'): + return self.result + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + def parse_domain(self, apikey): + feature = 'dnslookup' + if requests.get(f'{self.url.format(feature, apikey)}stats').json()['credits_remained'] < 0.13: + self.result = {'error': 'You do not have enough APIVoid credits to proceed your request.'} + return + mapping = {'A': 'resolution-of', 'MX': 'mail-server-of', 'NS': 'server-name-of'} + dnslookup = requests.get(f'{self.url.format(feature, apikey)}action=dns-any&host={self.attribute.value}').json() + for item in dnslookup['data']['records']['items']: + record_type = item['type'] + try: + relationship = mapping[record_type] + except KeyError: + continue + self._handle_dns_record(item, record_type, relationship) + ssl = requests.get(f'{self.url.format("sslinfo", apikey)}host={self.attribute.value}').json() + self._parse_ssl_certificate(ssl['data']['certificate']) + + def _handle_dns_record(self, item, record_type, relationship): + dns_record = MISPObject('dns-record') + dns_record.add_attribute('queried-domain', type='domain', value=item['host']) + attribute_type, feature = ('ip-dst', 'ip') if record_type == 'A' else ('domain', 'target') + dns_record.add_attribute(f'{record_type.lower()}-record', type=attribute_type, value=item[feature]) + dns_record.add_reference(self.attribute.uuid, relationship) + self.misp_event.add_object(**dns_record) + + def _parse_ssl_certificate(self, certificate): + x509 = MISPObject('x509') + fingerprint = 'x509-fingerprint-sha1' + x509.add_attribute(fingerprint, type=fingerprint, value=certificate['fingerprint']) + x509_mapping = {'subject': {'name': ('text', 'subject')}, + 'issuer': {'common_name': ('text', 'issuer')}, + 'signature': {'serial': ('text', 'serial-number')}, + 'validity': {'valid_from': ('datetime', 'validity-not-before'), + 'valid_to': ('datetime', 'validity-not-after')}} + certificate = certificate['details'] + for feature, subfeatures in x509_mapping.items(): + for subfeature, mapping in subfeatures.items(): + attribute_type, relation = mapping + x509.add_attribute(relation, type=attribute_type, value=certificate[feature][subfeature]) + x509.add_reference(self.attribute.uuid, 'seen-by') + self.misp_event.add_object(**x509) + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if not request.get('config', {}).get('apikey'): + return {'error': 'An API key for APIVoid is required.'} + attribute = request.get('attribute') + apikey = request['config']['apikey'] + apivoid_parser = APIVoidParser(attribute) + apivoid_parser.parse_domain(apikey) + return apivoid_parser.get_results() + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 0d3e61dc4d82ad74c7df32bde9436defd5697d15 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Dec 2019 23:04:36 +0100 Subject: [PATCH 063/220] add: [tests] Test case for the APIVoid module --- tests/test_expansions.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 79aa401..93ee69d 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -78,6 +78,24 @@ class TestExpansions(unittest.TestCase): except AssertionError: self.assertTrue(self.get_values(response).startswith('185.255.79.90 IS NOT listed by OSINT.digitalside.it.')) + def test_apivoid(self): + module_name = "apivoid" + query = {"module": module_name, + "attribute": {"type": "domain", + "value": "circl.lu", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}, + "config": {}} + if module_name in self.configs: + query['config'] = self.configs[module_name] + response = self.misp_modules_post(query) + try: + self.assertEqual(self.get_object(response), 'dns-record') + except Exception: + self.assertTrue(self.get_errors(response).startswith('You do not have enough APIVoid credits')) + else: + response = self.misp_modules_post(query) + self.assertEqual(self.get_errors(response), 'An API key for APIVoid is required.') + def test_bgpranking(self): query = {"module": "bgpranking", "AS": "13335"} response = self.misp_modules_post(query) From 9679fed7b52f3add84ff3bd5981a50b8fe20a7ac Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 19 Dec 2019 09:24:16 +0100 Subject: [PATCH 064/220] add: Documentation for the new API Void module --- README.md | 1 + doc/README.md | 20 ++++++++++++++++++++ doc/expansion/apivoid.json | 9 +++++++++ 3 files changed, 30 insertions(+) create mode 100644 doc/expansion/apivoid.json diff --git a/README.md b/README.md index 38ab966..d0296a8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj ### Expansion modules * [apiosintDS](misp_modules/modules/expansion/apiosintds.py) - a hover and expansion module to query the OSINT.digitalside.it API. +* [API Void](misp_modules/modules/expansion/apivoid.py) - an expansion and hover module to query API Void with a domain attribute. * [AssemblyLine submit](misp_modules/modules/expansion/assemblyline_submit.py) - an expansion module to submit samples and urls to AssemblyLine. * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. diff --git a/doc/README.md b/doc/README.md index eb5e89d..64df950 100644 --- a/doc/README.md +++ b/doc/README.md @@ -22,6 +22,26 @@ On demand query API for OSINT.digitalside.it project. ----- +#### [apivoid](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/apivoid.py) + + + +Module to query APIVoid with some domain attributes. +- **features**: +>This module takes a domain name and queries API Void to get the related DNS records and the SSL certificates. It returns then those pieces of data as MISP objects that can be added to the event. +> +>To make it work, a valid API key and enough credits to proceed 2 queries (0.06 + 0.07 credits) are required. +- **input**: +>A domain attribute. +- **output**: +>DNS records and SSL certificates related to the domain. +- **references**: +>https://www.apivoid.com/ +- **requirements**: +>A valid APIVoid API key with enough credits to proceed 2 queries + +----- + #### [assemblyline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_query.py) diff --git a/doc/expansion/apivoid.json b/doc/expansion/apivoid.json new file mode 100644 index 0000000..2173d5b --- /dev/null +++ b/doc/expansion/apivoid.json @@ -0,0 +1,9 @@ +{ + "description": "Module to query APIVoid with some domain attributes.", + "logo": "logos/apivoid.png", + "requirements": ["A valid APIVoid API key with enough credits to proceed 2 queries"], + "input": "A domain attribute.", + "output": "DNS records and SSL certificates related to the domain.", + "features": "This module takes a domain name and queries API Void to get the related DNS records and the SSL certificates. It returns then those pieces of data as MISP objects that can be added to the event.\n\nTo make it work, a valid API key and enough credits to proceed 2 queries (0.06 + 0.07 credits) are required.", + "references": ["https://www.apivoid.com/"] +} From 0d80d5fdfa3ece513bba17f097738570f5b5143b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 19 Dec 2019 17:06:23 +0100 Subject: [PATCH 065/220] fix: [doc] Added APIVoid logo --- doc/logos/apivoid.png | Bin 0 -> 6955 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/logos/apivoid.png diff --git a/doc/logos/apivoid.png b/doc/logos/apivoid.png new file mode 100644 index 0000000000000000000000000000000000000000..e4f84a7c70228f40b8e69d8aafa565becd01ba5a GIT binary patch literal 6955 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 literal 0 HcmV?d00001 From cf5ad29f270c5ade9d63b8f47d2a41366ff04168 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 7 Jan 2020 17:03:10 +0100 Subject: [PATCH 066/220] chg: Checking attributes category - We check the category before adding the attribute to the event - Checking if the category is correct and if not, doing a case insensitive check - If the category is not correct after the 2 first tests, we simply delete it from the attribute and pymisp will give the attribute a default category value based on the atttribute type, at the creation of the attribute --- misp_modules/modules/import_mod/csvimport.py | 67 ++++++++++++++++---- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index 96e42b1..8bfbbe9 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -34,7 +34,7 @@ misp_extended_csv_header = misp_standard_csv_header + misp_context_additional_fi class CsvParser(): - def __init__(self, header, has_header, delimiter, data, from_misp, MISPtypes): + def __init__(self, header, has_header, delimiter, data, from_misp, MISPtypes, categories): self.misp_event = MISPEvent() self.header = header self.has_header = has_header @@ -42,11 +42,16 @@ class CsvParser(): self.data = data self.from_misp = from_misp self.MISPtypes = MISPtypes + self.categories = categories self.fields_number = len(self.header) - self.__score_mapping = {0: self.__create_standard_misp, + self.__score_mapping = {0: self.__create_standard_attribute, 1: self.__create_attribute_with_ids, 2: self.__create_attribute_with_tags, - 3: self.__create_attribute_with_ids_and_tags} + 3: self.__create_attribute_with_ids_and_tags, + 4: self.__create_attribute_check_category, + 5: self.__create_attribute_check_category_and_ids, + 6: self.__create_attribute_check_category_and_tags, + 7: self.__create_attribute_check_category_with_ids_and_tags} def parse_csv(self): if self.from_misp: @@ -165,35 +170,68 @@ class CsvParser(): # Utility functions # ################################################################################ + def __create_attribute_check_category(self, line, indexes): + attribute = self.__create_standard_attribute(line, indexes) + self.__check_category(attribute) + return attribute + + def __create_attribute_check_category_and_ids(self, line, indexes): + attribute = self.__create_attribute_with_ids(line, indexes) + self.__check_category(attribute) + return attribute + + def __create_attribute_check_category_and_tags(self, line, indexes): + attribute = self.__create_attribute_with_tags(line, indexes) + self.__check_category(attribute) + return attribute + + def __create_attribute_check_category_with_ids_and_tags(self, line, indexes): + attribute = self.__create_attribute_with_ids_and_tags(line, indexes) + self.__check_category(attribute) + return attribute + def __create_attribute_with_ids(self, line, indexes): - attribute = self.__create_standard_misp(line, indexes) - return self.__deal_with_ids(attribute) + attribute = self.__create_standard_attribute(line, indexes) + self.__deal_with_ids(attribute) + return attribute def __create_attribute_with_ids_and_tags(self, line, indexes): - attribute = self.__deal_with_ids(self.__create_standard_misp(line, indexes)) - return self.__deal_with_tags(attribute) + attribute = self.__create_standard_attribute(line, indexes) + self.__deal_with_ids(attribute) + self.__deal_with_tags(attribute) + return attribute def __create_attribute_with_tags(self, line, indexes): - attribute = self.__create_standard_misp(line, indexes) - return self.__deal_with_tags(attribute) + attribute = self.__create_standard_attribute(line, indexes) + self.__deal_with_tags(attribute) + return attribute - def __create_standard_misp(self, line, indexes): + def __create_standard_attribute(self, line, indexes): return {self.header[index]: line[index] for index in indexes if line[index]} + def __check_category(self, attribute): + category = attribute['category'] + if category in self.categories: + return + if category.capitalize() in self.categories: + attribute['category'] = category.capitalize() + return + del attribute['category'] + @staticmethod def __deal_with_ids(attribute): attribute['to_ids'] = True if attribute['to_ids'] == '1' else False - return attribute @staticmethod def __deal_with_tags(attribute): attribute['Tag'] = [{'name': tag.strip()} for tag in attribute['Tag'].split(',')] - return attribute def __get_score(self): score = 1 if 'to_ids' in self.header else 0 if 'attribute_tag' in self.header: score += 2 + if 'category' in self.header: + score += 4 return score def __finalize_results(self): @@ -241,7 +279,8 @@ def handler(q=False): header = misp_standard_csv_header descFilename = os.path.join(pymisp_path[0], 'data/describeTypes.json') with open(descFilename, 'r') as f: - MISPtypes = json.loads(f.read())['result'].get('types') + description = json.loads(f.read())['result'] + MISPtypes = description['types'] for h in header: if not any((h in MISPtypes, h in misp_extended_csv_header, h in ('', ' ', '_', 'object_id'))): misperrors['error'] = 'Wrong header field: {}. Please use a header value that can be recognized by MISP (or alternatively skip it using a whitespace).'.format(h) @@ -256,7 +295,7 @@ def handler(q=False): wrong_types = tuple(wrong_type for wrong_type in ('type', 'value') if wrong_type in header) misperrors['error'] = 'Error with the following header: {}. It contains the following field(s): {}, which is(are) already provided by the usage of at least on MISP attribute type in the header.'.format(header, 'and'.join(wrong_types)) return misperrors - csv_parser = CsvParser(header, has_header, delimiter, data, from_misp, MISPtypes) + csv_parser = CsvParser(header, has_header, delimiter, data, from_misp, MISPtypes, description['categories']) # build the attributes result = csv_parser.parse_csv() if 'error' in result: From bfcba18e3c0e682512504aa6d82990a83a1adde6 Mon Sep 17 00:00:00 2001 From: Erick Cheng Date: Tue, 7 Jan 2020 18:58:40 +0100 Subject: [PATCH 067/220] Update ipasn.py --- misp_modules/modules/expansion/ipasn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/ipasn.py b/misp_modules/modules/expansion/ipasn.py index 8489aa0..cfdbaf5 100755 --- a/misp_modules/modules/expansion/ipasn.py +++ b/misp_modules/modules/expansion/ipasn.py @@ -28,7 +28,7 @@ def handler(q=False): if not values: misperrors['error'] = 'Unable to find the history of this IP' return misperrors - return {'results': [{'types': mispattributes['output'], 'values': values}]} + return {'results': [{'types': mispattributes['output'], 'values': [str(values)]}]} def introspection(): From 10b4e78704c273974f0cd9cb4a85678fbb53775c Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Thu, 9 Jan 2020 09:57:46 +0000 Subject: [PATCH 068/220] add vt_graph export module --- misp_modules/lib/vt_graph_parser/__init__.py | 12 + misp_modules/lib/vt_graph_parser/errors.py | 20 ++ .../lib/vt_graph_parser/helpers/__init__.py | 4 + .../lib/vt_graph_parser/helpers/parsers.py | 89 +++++ .../lib/vt_graph_parser/helpers/rules.py | 304 ++++++++++++++++++ .../lib/vt_graph_parser/helpers/wrappers.py | 59 ++++ .../lib/vt_graph_parser/importers/__init__.py | 12 + .../lib/vt_graph_parser/importers/base.py | 98 ++++++ .../importers/pymisp_response.py | 75 +++++ misp_modules/modules/export_mod/__init__.py | 2 +- misp_modules/modules/export_mod/vt_graph.py | 113 +++++++ 11 files changed, 787 insertions(+), 1 deletion(-) create mode 100644 misp_modules/lib/vt_graph_parser/__init__.py create mode 100644 misp_modules/lib/vt_graph_parser/errors.py create mode 100644 misp_modules/lib/vt_graph_parser/helpers/__init__.py create mode 100644 misp_modules/lib/vt_graph_parser/helpers/parsers.py create mode 100644 misp_modules/lib/vt_graph_parser/helpers/rules.py create mode 100644 misp_modules/lib/vt_graph_parser/helpers/wrappers.py create mode 100644 misp_modules/lib/vt_graph_parser/importers/__init__.py create mode 100644 misp_modules/lib/vt_graph_parser/importers/base.py create mode 100644 misp_modules/lib/vt_graph_parser/importers/pymisp_response.py create mode 100644 misp_modules/modules/export_mod/vt_graph.py diff --git a/misp_modules/lib/vt_graph_parser/__init__.py b/misp_modules/lib/vt_graph_parser/__init__.py new file mode 100644 index 0000000..2a4d339 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/__init__.py @@ -0,0 +1,12 @@ +"""vt_graph_parser. + +This module provides methods to import graph from misp. +""" + + +from lib.vt_graph_parser.importers import from_pymisp_response + + +__all__ = [ + "from_pymisp_response" +] diff --git a/misp_modules/lib/vt_graph_parser/errors.py b/misp_modules/lib/vt_graph_parser/errors.py new file mode 100644 index 0000000..4063933 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/errors.py @@ -0,0 +1,20 @@ +"""vt_graph_parser.errors. + +This module provides custom errors for data importers. +""" + + +class GraphImportError(Exception): + pass + + +class InvalidFileFormatError(Exception): + pass + + +class MispEventNotFoundError(Exception): + pass + + +class ServerError(Exception): + pass diff --git a/misp_modules/lib/vt_graph_parser/helpers/__init__.py b/misp_modules/lib/vt_graph_parser/helpers/__init__.py new file mode 100644 index 0000000..336faee --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/helpers/__init__.py @@ -0,0 +1,4 @@ +"""vt_graph_parser.helpers. + +This modules provides functions and attributes to help MISP importers. +""" diff --git a/misp_modules/lib/vt_graph_parser/helpers/parsers.py b/misp_modules/lib/vt_graph_parser/helpers/parsers.py new file mode 100644 index 0000000..ef78313 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/helpers/parsers.py @@ -0,0 +1,89 @@ +"""vt_graph_parser.helpers.parsers. + +This module provides parsers for MISP inputs. +""" + + +from lib.vt_graph_parser.helpers.wrappers import MispAttribute + + +MISP_INPUT_ATTR = [ + "hostname", + "domain", + "ip-src", + "ip-dst", + "md5", + "sha1", + "sha256", + "url", + "filename|md5", + "filename", + "target-user", + "target-email" +] + +VIRUSTOTAL_GRAPH_LINK_PREFIX = "https://www.virustotal.com/graph/" + + +def _parse_data(attributes, objects): + """Parse MISP event attributes and objects data. + + Args: + attributes (dict): dictionary which contains the MISP event attributes data. + objects (dict): dictionary which contains the MISP event objects data. + + Returns: + ([MispAttribute], str): MISP attributes and VTGraph link if exists. + Link defaults to "". + """ + attributes_data = [] + vt_graph_link = "" + + # Get simple MISP event attributes. + attributes_data += ( + [attr for attr in attributes + if attr.get("type") in MISP_INPUT_ATTR]) + + # Get attributes from MISP objects too. + if objects: + for object_ in objects: + object_attrs = object_.get("Attribute", []) + attributes_data += ( + [attr for attr in object_attrs + if attr.get("type") in MISP_INPUT_ATTR]) + + # Check if there is any VirusTotal Graph computed in MISP event. + vt_graph_links = ( + attr for attr in attributes if attr.get("type") == "link" + and attr.get("value", "").startswith(VIRUSTOTAL_GRAPH_LINK_PREFIX)) + + # MISP could have more than one VirusTotal Graph, so we will take + # the last one. + current_id = 0 # MISP attribute id is the number of the attribute. + vt_graph_link = "" + for link in vt_graph_links: + if int(link.get("id")) > current_id: + current_id = int(link.get("id")) + vt_graph_link = link.get("value") + + attributes = [ + MispAttribute(data["type"], data["category"], data["value"]) + for data in attributes_data] + return (attributes, + vt_graph_link.replace(VIRUSTOTAL_GRAPH_LINK_PREFIX, "")) + + +def parse_pymisp_response(payload): + """Get event attributes and VirusTotal Graph id from pymisp response. + + Args: + payload (dict): dictionary which contains pymisp response. + + Returns: + ([MispAttribute], str): MISP attributes and VTGraph link if exists. + Link defaults to "". + """ + event_attrs = payload.get("Attribute", []) + objects = payload.get("Object") + return _parse_data(event_attrs, objects) + diff --git a/misp_modules/lib/vt_graph_parser/helpers/rules.py b/misp_modules/lib/vt_graph_parser/helpers/rules.py new file mode 100644 index 0000000..14230d0 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/helpers/rules.py @@ -0,0 +1,304 @@ +"""vt_graph_parser.helpers.rules. + +This module provides rules that helps MISP importers to connect MISP attributes +between them using VirusTotal relationship. Check all available relationship +here: + +- File: https://developers.virustotal.com/v3/reference/#files-relationships +- URL: https://developers.virustotal.com/v3/reference/#urls-relationships +- Domain: https://developers.virustotal.com/v3/reference/#domains-relationships +- IP: https://developers.virustotal.com/v3/reference/#ip-relationships +""" + + +import abc + + +class MispEventRule(object): + """Rules for MISP event nodes connection object wrapper.""" + + def __init__(self, last_rule=None, node=None): + """Create a MispEventRule instance. + + MispEventRule is a collection of rules that can infer the relationships + between nodes from MISP events. + + Args: + last_rule (MispEventRule): previous rule. + node (Node): actual node. + """ + self.last_rule = last_rule + self.node = node + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def get_last_different_rule(self): + """Search the last rule whose event was different from actual. + + Returns: + MispEventRule: the last different rule. + """ + if not isinstance(self, self.last_rule.__class__): + return self.last_rule + else: + return self.last_rule.get_last_different_rule() + + def resolve_relation(self, graph, node, misp_category): + """Try to infer a relationship between two nodes. + + This method is based on a non-deterministic finite automaton for + this reason the future rule only depends on the actual rule and the input + node. + + For example if the actual rule is a MISPEventDomainRule and the given node + is an ip_address node, the connection type between them will be + `resolutions` and the this rule will transit to MISPEventIPRule. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. + + Returns: + MispEventRule: the transited rule. + """ + if node.node_type in self.relation_event: + return self.relation_event[node.node_type](graph, node, misp_category) + else: + return self.manual_link(graph, node) + + def manual_link(self, graph, node): + """Creates a manual link between self.node and the given node. + + We accept MISP types that VirusTotal does not know how to link, so we create + a end to end relationship instead of create an unknown relationship node. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + + Returns: + MispEventRule: the transited rule. + """ + graph.add_link(self.node.node_id, node.node_id, "manual") + return self + + @abc.abstractmethod + def __file_transition(self, graph, node, misp_category): + """Make a new transition due to file attribute event. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. + + Returns: + MispEventRule: the transited rule. + """ + pass + + @abc.abstractmethod + def __ip_transition(self, graph, node, misp_category): + """Make a new transition due to ip attribute event. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. + + Returns: + MispEventRule: the transited rule. + """ + pass + + @abc.abstractmethod + def __url_transition(self, graph, node, misp_category): + """Make a new transition due to url attribute event. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. + + Returns: + MispEventRule: the transited rule. + """ + pass + + @abc.abstractmethod + def __domain_transition(self, graph, node, misp_category): + """Make a new transition due to domain attribute event. + + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. + + Returns: + MispEventRule: the transited rule. + """ + pass + + +class MispEventURLRule(MispEventRule): + """Rule for URL event.""" + + def __init__(self, last_rule=None, node=None): + super(MispEventURLRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def __file_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "downloaded_files") + return MispEventFileRule(self, node) + + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_ips") + return MispEventIPRule(self, node) + + def __url_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventURLRule(self, node) + + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_domains") + return MispEventDomainRule(self, node) + + +class MispEventIPRule(MispEventRule): + """Rule for IP event.""" + + def __init__(self, last_rule=None, node=None): + super(MispEventIPRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def __file_transition(self, graph, node, misp_category): + connection_type = "communicating_files" + if misp_category == "Artifacts dropped": + connection_type = "downloaded_files" + graph.add_link(self.node.node_id, node.node_id, connection_type) + return MispEventFileRule(self, node) + + def __ip_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventIPRule(self, node) + + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "urls") + return MispEventURLRule(self, node) + + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "resolutions") + return MispEventDomainRule(self, node) + + +class MispEventDomainRule(MispEventRule): + """Rule for domain event.""" + + def __init__(self, last_rule=None, node=None): + super(MispEventDomainRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def __file_transition(self, graph, node, misp_category): + connection_type = "communicating_files" + if misp_category == "Artifacts dropped": + connection_type = "downloaded_files" + graph.add_link(self.node.node_id, node.node_id, connection_type) + return MispEventFileRule(self, node) + + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "resolutions") + return MispEventIPRule(self, node) + + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "urls") + return MispEventURLRule(self, node) + + def __domain_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + graph.add_link(self.node.node_id, node.node_id, "siblings") + return MispEventDomainRule(self, node) + + +class MispEventFileRule(MispEventRule): + """Rule for File event.""" + + def __init__(self, last_rule=None, node=None): + super(MispEventFileRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def __file_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventFileRule(self, node) + + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_ips") + return MispEventIPRule(self, node) + + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_urls") + return MispEventURLRule(self, node) + + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_domains") + return MispEventDomainRule(self, node) + + +class MispEventInitialRule(MispEventRule): + """Initial rule.""" + + def __init__(self, last_rule=None, node=None): + super(MispEventInitialRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } + + def __file_transition(self, graph, node, misp_category): + return MispEventFileRule(self, node) + + def __ip_transition(self, graph, node, misp_category): + return MispEventIPRule(self, node) + + def __url_transition(self, graph, node, misp_category): + return MispEventURLRule(self, node) + + def __domain_transition(self, graph, node, misp_category): + return MispEventDomainRule(self, node) diff --git a/misp_modules/lib/vt_graph_parser/helpers/wrappers.py b/misp_modules/lib/vt_graph_parser/helpers/wrappers.py new file mode 100644 index 0000000..8735317 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/helpers/wrappers.py @@ -0,0 +1,59 @@ +"""vt_graph_parser.helpers.wrappers. + +This module provides a Python object wrapper for MISP objects. +""" + + +class MispAttribute(object): + """Python object wrapper for MISP attribute. + + Attributes: + type (str): VirusTotal node type. + category (str): MISP attribute category. + value (str): node id. + label (str): node name. + misp_type (str): MISP node type. + """ + + MISP_TYPES_REFERENCE = { + "hostname": "domain", + "domain": "domain", + "ip-src": "ip_address", + "ip-dst": "ip_address", + "url": "url", + "filename|X": "file", + "filename": "file", + "md5": "file", + "sha1": "file", + "sha256": "file", + "target-user": "victim", + "target-email": "email" + } + + def __init__(self, misp_type, category, value, label=""): + """Constructor for a MispAttribute. + + Args: + misp_type (str): MISP type attribute. + category (str): MISP category attribute. + value (str): attribute value. + label (str): attribute label. + """ + if misp_type.startswith("filename|"): + label, value = value.split("|") + misp_type = "filename|X" + if misp_type == "filename": + label = value + + self.type = self.MISP_TYPES_REFERENCE.get(misp_type) + self.category = category + self.value = value + self.label = label + self.misp_type = misp_type + + def __eq__(self, other): + return (isinstance(other, self.__class__) and self.value == other.value and + self.type == other.type) + + def __repr__(self): + return 'MispAttribute("{type}", "{category}", "{value}")'.format(type=self.type, category=self.category, value=self.value) diff --git a/misp_modules/lib/vt_graph_parser/importers/__init__.py b/misp_modules/lib/vt_graph_parser/importers/__init__.py new file mode 100644 index 0000000..129d870 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/importers/__init__.py @@ -0,0 +1,12 @@ +"""vt_graph_parser.importers. + +This module provides methods to import graphs from MISP. +""" + + +from lib.vt_graph_parser.importers.pymisp_response import from_pymisp_response + + +__all__ = [ + "from_pymisp_response" +] diff --git a/misp_modules/lib/vt_graph_parser/importers/base.py b/misp_modules/lib/vt_graph_parser/importers/base.py new file mode 100644 index 0000000..cdea8b6 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/importers/base.py @@ -0,0 +1,98 @@ +"""vt_graph_parser.importers.base. + +This module provides a common method to import graph from misp attributes. +""" + + +import vt_graph_api +from lib.vt_graph_parser.helpers.rules import MispEventRule + + +def import_misp_graph( + misp_attributes, graph_id, vt_api_key, fetch_information, name, + private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, + group_viewers, use_vt_to_connect_the_graph, max_api_quotas, + max_search_depth): + """Import VirusTotal Graph from MISP. + + Args: + misp_attributes ([MispAttribute]): list with the MISP attributes which + will be added to the returned graph. + graph_id: if supplied, the graph will be loaded instead of compute it again. + vt_api_key (str): VT API Key. + fetch_information (bool): whether the script will fetch + information for added nodes in VT. Defaults to True. + name (str): graph title. Defaults to "". + private (bool): True for private graphs. You need to have + Private Graph premium features enabled in your subscription. Defaults + to False. + fetch_vt_enterprise (bool, optional): if True, the graph will search any + available information using VirusTotal Intelligence for the node if there + is no normal information for it. Defaults to False. + user_editors ([str]): usernames that can edit the graph. + Defaults to None. + user_viewers ([str]): usernames that can view the graph. + Defaults to None. + group_editors ([str]): groups that can edit the graph. + Defaults to None. + group_viewers ([str]): groups that can view the graph. + Defaults to None. + use_vt_to_connect_the_graph (bool): if True, graph nodes will + be linked using VirusTotal API. Otherwise, the links will be generated + using production rules based on MISP attributes order. Defaults to + False. + max_api_quotas (int): maximum number of api quotas that could + be consumed to resolve graph using VirusTotal API. Defaults to 20000. + max_search_depth (int, optional): max search depth to explore + relationship between nodes when use_vt_to_connect_the_graph is True. + Defaults to 3. + + If use_vt_to_connect_the_graph is True, it will take some time to compute + graph. + + Returns: + vt_graph_api.graph.VTGraph: the imported graph. + """ + + rule = MispEventInitialRule() + + # Check if the event has been already computed in VirusTotal Graph. Otherwise + # a new graph will be created. + if not graph_id: + graph = vt_graph_api.VTGraph( + api_key=vt_api_key, name=name, private=private, + user_editors=user_editors, user_viewers=user_viewers, + group_editors=group_editors, group_viewers=group_viewers) + else: + graph = vt_graph_api.VTGraph.load_graph(graph_id, vt_api_key) + + attributes_to_add = [attr for attr in misp_attributes + if not graph.has_node(attr.value)] + + total_expandable_attrs = max(sum( + 1 for attr in attributes_to_add + if attr.type in vt_graph_api.Node.SUPPORTED_NODE_TYPES), + 1) + + max_quotas_per_search = max(int(max_api_quotas / total_expandable_attrs), 1) + + previous_node_id = "" + for attr in attributes_to_add: + # Add the current attr as node to the graph. + added_node = graph.add_node( + attr.value, attr.type, fetch_information, fetch_vt_enterprise, + attr.label) + # If use_vt_to_connect_the_grap is True the nodes will be connected using + # VT API. + if use_vt_to_connect_the_graph: + if (attr.type not in vt_graph_api.Node.SUPPORTED_NODE_TYPES and + previous_node_id): + graph.add_link(previous_node_id, attr.value, "manual") + else: + graph.connect_with_graph( + attr.value, max_quotas_per_search, max_search_depth, + fetch_info_collected_nodes=fetch_information) + else: + rule = rule.resolve_relation(graph, added_node, attr.category) + + return graph diff --git a/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py new file mode 100644 index 0000000..c01b6a1 --- /dev/null +++ b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py @@ -0,0 +1,75 @@ +"""vt_graph_parser.importers.pymisp_response. + +This modules provides a graph importer method for MISP event by using the +response payload giving by MISP API directly. +""" + + +import json +from lib.vt_graph_parser import errors +from lib.vt_graph_parser.helpers.parsers import parse_pymisp_response +from lib.vt_graph_parser.importers.base import import_misp_graph + + +def from_pymisp_response( + payload, vt_api_key, fetch_information=True, + private=False, fetch_vt_enterprise=False, user_editors=None, + user_viewers=None, group_editors=None, group_viewers=None, + use_vt_to_connect_the_graph=False, max_api_quotas=1000, + max_search_depth=3, expand_node_one_level=False): + """Import VirusTotal Graph from MISP JSON file. + + Args: + payload (dict): dictionary which contains the request payload. + vt_api_key (str): VT API Key. + fetch_information (bool, optional): whether the script will fetch + information for added nodes in VT. Defaults to True. + name (str, optional): graph title. Defaults to "". + private (bool, optional): True for private graphs. You need to have + Private Graph premium features enabled in your subscription. Defaults + to False. + fetch_vt_enterprise (bool, optional): if True, the graph will search any + available information using VirusTotal Intelligence for the node if there + is no normal information for it. Defaults to False. + user_editors ([str], optional): usernames that can edit the graph. + Defaults to None. + user_viewers ([str], optional): usernames that can view the graph. + Defaults to None. + group_editors ([str], optional): groups that can edit the graph. + Defaults to None. + group_viewers ([str], optional): groups that can view the graph. + Defaults to None. + use_vt_to_connect_the_graph (bool, optional): if True, graph nodes will + be linked using VirusTotal API. Otherwise, the links will be generated + using production rules based on MISP attributes order. Defaults to + False. + max_api_quotas (int, optional): maximum number of api quotas that could + be consumed to resolve graph using VirusTotal API. Defaults to 20000. + max_search_depth (int, optional): max search depth to explore + relationship between nodes when use_vt_to_connect_the_graph is True. + Defaults to 3. + expand_one_level (bool, optional): expand entire graph one level. + Defaults to False. + + If use_vt_to_connect_the_graph is True, it will take some time to compute + graph. + + Raises: + LoaderError: if JSON file is invalid. + + Returns: + [vt_graph_api.graph.VTGraph: the imported graph]. + """ + graphs = [] + for event_payload in payload['data']: + misp_attrs, graph_id = parse_pymisp_response(event_payload) + name = "Graph created from MISP event" + graph = import_misp_graph( + misp_attrs, graph_id, vt_api_key, fetch_information, name, + private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, + group_viewers, use_vt_to_connect_the_graph, max_api_quotas, + max_search_depth) + if expand_node_one_level: + graph.expand_n_level(1) + graphs.append(graph) + return graphs diff --git a/misp_modules/modules/export_mod/__init__.py b/misp_modules/modules/export_mod/__init__.py index 77dec0d..1b0e1d0 100644 --- a/misp_modules/modules/export_mod/__init__.py +++ b/misp_modules/modules/export_mod/__init__.py @@ -1,2 +1,2 @@ __all__ = ['cef_export', 'mass_eql_export', 'liteexport', 'goamlexport', 'threat_connect_export', 'pdfexport', - 'threatStream_misp_export', 'osqueryexport', 'nexthinkexport'] + 'threatStream_misp_export', 'osqueryexport', 'nexthinkexport', 'vt_graph'] diff --git a/misp_modules/modules/export_mod/vt_graph.py b/misp_modules/modules/export_mod/vt_graph.py new file mode 100644 index 0000000..9d20a00 --- /dev/null +++ b/misp_modules/modules/export_mod/vt_graph.py @@ -0,0 +1,113 @@ +'''Export MISP event to VirusTotal Graph.''' + + +import base64 +import json +from lib.vt_graph_parser import from_pymisp_response + + +misperrors = { + 'error': 'Error' +} +moduleinfo = { + 'version': '0.1', + 'author': 'VirusTotal', + 'description': 'Send event to VirusTotal Graph', + 'module-type': ['export'] +} +mispattributes = { + 'input': [ + 'hostname', + 'domain', + 'ip-src', + 'ip-dst', + 'md5', + 'sha1', + 'sha256', + 'url', + 'filename|md5', + 'filename' + ] +} +moduleconfig = [ + 'vt_api_key', + 'fetch_information', + 'private', + 'fetch_vt_enterprise', + 'expand_one_level', + 'user_editors', + 'user_viewers', + 'group_editors', + 'group_viewers' +] + + +def handler(q=False): + """Expansion handler. + + Args: + q (bool, optional): module data. Defaults to False. + + Returns: + [str]: VirusTotal graph links + """ + if not q: + return False + request = json.loads(q) + + if not request.get('config') or not request['config'].get('vt_api_key'): + misperrors['error'] = 'A VirusTotal api key is required for this module.' + return misperrors + + config = request['config'] + + api_key = config.get('vt_api_key') + fetch_information = config.get('fetch_information') or False + private = config.get('private') or False + fetch_vt_enterprise = config.get('fetch_vt_enterprise') or False + expand_one_level = config.get('expand_one_level') or False + + user_editors = config.get('user_editors') + if user_editors: + user_editors = user_editors.split(',') + user_viewers = config.get('user_viewers') + if user_viewers: + user_viewers = user_viewers.split(',') + group_editors = config.get('group_editors') + if group_editors: + group_editors = group_editors.split(',') + group_viewers = config.get('group_viewers') + if group_viewers: + group_viewers = group_viewers.split(',') + + + graphs = from_pymisp_response( + request, api_key, fetch_information=fetch_information, + private=private, fetch_vt_enterprise=fetch_vt_enterprise, + user_editors=user_editors, user_viewers=user_viewers, + group_editors=group_editors, group_viewers=group_viewers, + expand_node_one_level=expand_one_level) + links = [] + + for graph in graphs: + graph.save_graph() + links.append(graph.get_ui_link()) + + # This file will contains one VirusTotal graph link for each exported event + file_data = str(base64.b64encode(bytes('\n'.join(links), 'utf-8')), 'utf-8') + return {'response': [], 'data': file_data} + + +def introspection(): + modulesetup = { + 'responseType': 'application/txt', + 'outputFileExtension': 'txt', + 'userConfig': {}, + 'inputSource': [] + } + return modulesetup + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 3207ceca046c3bb3faf296ee35ca1fe2785df2fc Mon Sep 17 00:00:00 2001 From: Alvaro Garcia Date: Thu, 9 Jan 2020 12:39:43 +0000 Subject: [PATCH 069/220] Add vt-graph-api to the requirements --- Pipfile | 1 + REQUIREMENTS | 1 + 2 files changed, 2 insertions(+) diff --git a/Pipfile b/Pipfile index 1a99c42..9e651de 100644 --- a/Pipfile +++ b/Pipfile @@ -59,6 +59,7 @@ jbxapi = "*" geoip2 = "*" apiosintDS = "*" assemblyline_client = "*" +vt-graph-api = "*" [requires] python_version = "3" diff --git a/REQUIREMENTS b/REQUIREMENTS index ee6c7c1..40b4caf 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -106,3 +106,4 @@ xlsxwriter==1.2.6 yara-python==3.8.1 yarl==1.4.2 zipp==0.6.0 +vt-graph-api From 7722e2cb93014c6e9c2c59f8939029084995ff79 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 9 Jan 2020 15:28:33 +0100 Subject: [PATCH 070/220] fix: Fixed typo on function import --- misp_modules/lib/vt_graph_parser/importers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/lib/vt_graph_parser/importers/base.py b/misp_modules/lib/vt_graph_parser/importers/base.py index cdea8b6..3cd0192 100644 --- a/misp_modules/lib/vt_graph_parser/importers/base.py +++ b/misp_modules/lib/vt_graph_parser/importers/base.py @@ -5,7 +5,7 @@ This module provides a common method to import graph from misp attributes. import vt_graph_api -from lib.vt_graph_parser.helpers.rules import MispEventRule +from lib.vt_graph_parser.helpers.rules import MispEventInitialRule def import_misp_graph( From 70b3079aa3a9eacb7348090da7a0a18d199f605f Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 9 Jan 2020 16:01:18 +0100 Subject: [PATCH 071/220] fix: Fixed pep8 in the new module and related libraries --- misp_modules/lib/vt_graph_parser/errors.py | 8 +- .../lib/vt_graph_parser/helpers/parsers.py | 97 ++-- .../lib/vt_graph_parser/helpers/rules.py | 442 +++++++++--------- .../lib/vt_graph_parser/helpers/wrappers.py | 93 ++-- .../lib/vt_graph_parser/importers/base.py | 160 +++---- .../importers/pymisp_response.py | 116 +++-- misp_modules/modules/export_mod/vt_graph.py | 106 ++--- 7 files changed, 509 insertions(+), 513 deletions(-) diff --git a/misp_modules/lib/vt_graph_parser/errors.py b/misp_modules/lib/vt_graph_parser/errors.py index 4063933..a7e18e9 100644 --- a/misp_modules/lib/vt_graph_parser/errors.py +++ b/misp_modules/lib/vt_graph_parser/errors.py @@ -5,16 +5,16 @@ This module provides custom errors for data importers. class GraphImportError(Exception): - pass + pass class InvalidFileFormatError(Exception): - pass + pass class MispEventNotFoundError(Exception): - pass + pass class ServerError(Exception): - pass + pass diff --git a/misp_modules/lib/vt_graph_parser/helpers/parsers.py b/misp_modules/lib/vt_graph_parser/helpers/parsers.py index ef78313..c621595 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/parsers.py +++ b/misp_modules/lib/vt_graph_parser/helpers/parsers.py @@ -26,64 +26,63 @@ VIRUSTOTAL_GRAPH_LINK_PREFIX = "https://www.virustotal.com/graph/" def _parse_data(attributes, objects): - """Parse MISP event attributes and objects data. + """Parse MISP event attributes and objects data. - Args: - attributes (dict): dictionary which contains the MISP event attributes data. - objects (dict): dictionary which contains the MISP event objects data. + Args: + attributes (dict): dictionary which contains the MISP event attributes data. + objects (dict): dictionary which contains the MISP event objects data. - Returns: - ([MispAttribute], str): MISP attributes and VTGraph link if exists. - Link defaults to "". - """ - attributes_data = [] - vt_graph_link = "" + Returns: + ([MispAttribute], str): MISP attributes and VTGraph link if exists. + Link defaults to "". + """ + attributes_data = [] + vt_graph_link = "" - # Get simple MISP event attributes. - attributes_data += ( - [attr for attr in attributes - if attr.get("type") in MISP_INPUT_ATTR]) + # Get simple MISP event attributes. + attributes_data += ( + [attr for attr in attributes + if attr.get("type") in MISP_INPUT_ATTR]) - # Get attributes from MISP objects too. - if objects: - for object_ in objects: - object_attrs = object_.get("Attribute", []) - attributes_data += ( - [attr for attr in object_attrs - if attr.get("type") in MISP_INPUT_ATTR]) + # Get attributes from MISP objects too. + if objects: + for object_ in objects: + object_attrs = object_.get("Attribute", []) + attributes_data += ( + [attr for attr in object_attrs + if attr.get("type") in MISP_INPUT_ATTR]) - # Check if there is any VirusTotal Graph computed in MISP event. - vt_graph_links = ( - attr for attr in attributes if attr.get("type") == "link" - and attr.get("value", "").startswith(VIRUSTOTAL_GRAPH_LINK_PREFIX)) + # Check if there is any VirusTotal Graph computed in MISP event. + vt_graph_links = ( + attr for attr in attributes if attr.get("type") == "link" + and attr.get("value", "").startswith(VIRUSTOTAL_GRAPH_LINK_PREFIX)) - # MISP could have more than one VirusTotal Graph, so we will take - # the last one. - current_id = 0 # MISP attribute id is the number of the attribute. - vt_graph_link = "" - for link in vt_graph_links: - if int(link.get("id")) > current_id: - current_id = int(link.get("id")) - vt_graph_link = link.get("value") + # MISP could have more than one VirusTotal Graph, so we will take + # the last one. + current_id = 0 # MISP attribute id is the number of the attribute. + vt_graph_link = "" + for link in vt_graph_links: + if int(link.get("id")) > current_id: + current_id = int(link.get("id")) + vt_graph_link = link.get("value") - attributes = [ - MispAttribute(data["type"], data["category"], data["value"]) - for data in attributes_data] - return (attributes, - vt_graph_link.replace(VIRUSTOTAL_GRAPH_LINK_PREFIX, "")) + attributes = [ + MispAttribute(data["type"], data["category"], data["value"]) + for data in attributes_data] + return (attributes, + vt_graph_link.replace(VIRUSTOTAL_GRAPH_LINK_PREFIX, "")) def parse_pymisp_response(payload): - """Get event attributes and VirusTotal Graph id from pymisp response. + """Get event attributes and VirusTotal Graph id from pymisp response. - Args: - payload (dict): dictionary which contains pymisp response. - - Returns: - ([MispAttribute], str): MISP attributes and VTGraph link if exists. - Link defaults to "". - """ - event_attrs = payload.get("Attribute", []) - objects = payload.get("Object") - return _parse_data(event_attrs, objects) + Args: + payload (dict): dictionary which contains pymisp response. + Returns: + ([MispAttribute], str): MISP attributes and VTGraph link if exists. + Link defaults to "". + """ + event_attrs = payload.get("Attribute", []) + objects = payload.get("Object") + return _parse_data(event_attrs, objects) diff --git a/misp_modules/lib/vt_graph_parser/helpers/rules.py b/misp_modules/lib/vt_graph_parser/helpers/rules.py index 14230d0..e3ed7f8 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/rules.py +++ b/misp_modules/lib/vt_graph_parser/helpers/rules.py @@ -15,290 +15,290 @@ import abc class MispEventRule(object): - """Rules for MISP event nodes connection object wrapper.""" + """Rules for MISP event nodes connection object wrapper.""" - def __init__(self, last_rule=None, node=None): - """Create a MispEventRule instance. + def __init__(self, last_rule=None, node=None): + """Create a MispEventRule instance. - MispEventRule is a collection of rules that can infer the relationships - between nodes from MISP events. + MispEventRule is a collection of rules that can infer the relationships + between nodes from MISP events. - Args: - last_rule (MispEventRule): previous rule. - node (Node): actual node. - """ - self.last_rule = last_rule - self.node = node - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + Args: + last_rule (MispEventRule): previous rule. + node (Node): actual node. + """ + self.last_rule = last_rule + self.node = node + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def get_last_different_rule(self): - """Search the last rule whose event was different from actual. + def get_last_different_rule(self): + """Search the last rule whose event was different from actual. - Returns: - MispEventRule: the last different rule. - """ - if not isinstance(self, self.last_rule.__class__): - return self.last_rule - else: - return self.last_rule.get_last_different_rule() + Returns: + MispEventRule: the last different rule. + """ + if not isinstance(self, self.last_rule.__class__): + return self.last_rule + else: + return self.last_rule.get_last_different_rule() - def resolve_relation(self, graph, node, misp_category): - """Try to infer a relationship between two nodes. + def resolve_relation(self, graph, node, misp_category): + """Try to infer a relationship between two nodes. - This method is based on a non-deterministic finite automaton for - this reason the future rule only depends on the actual rule and the input - node. + This method is based on a non-deterministic finite automaton for + this reason the future rule only depends on the actual rule and the input + node. - For example if the actual rule is a MISPEventDomainRule and the given node - is an ip_address node, the connection type between them will be - `resolutions` and the this rule will transit to MISPEventIPRule. + For example if the actual rule is a MISPEventDomainRule and the given node + is an ip_address node, the connection type between them will be + `resolutions` and the this rule will transit to MISPEventIPRule. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. - misp_category: (str): MISP category of the given node. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. - Returns: - MispEventRule: the transited rule. - """ - if node.node_type in self.relation_event: - return self.relation_event[node.node_type](graph, node, misp_category) - else: - return self.manual_link(graph, node) + Returns: + MispEventRule: the transited rule. + """ + if node.node_type in self.relation_event: + return self.relation_event[node.node_type](graph, node, misp_category) + else: + return self.manual_link(graph, node) - def manual_link(self, graph, node): - """Creates a manual link between self.node and the given node. + def manual_link(self, graph, node): + """Creates a manual link between self.node and the given node. - We accept MISP types that VirusTotal does not know how to link, so we create - a end to end relationship instead of create an unknown relationship node. + We accept MISP types that VirusTotal does not know how to link, so we create + a end to end relationship instead of create an unknown relationship node. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. - Returns: - MispEventRule: the transited rule. - """ - graph.add_link(self.node.node_id, node.node_id, "manual") - return self + Returns: + MispEventRule: the transited rule. + """ + graph.add_link(self.node.node_id, node.node_id, "manual") + return self - @abc.abstractmethod - def __file_transition(self, graph, node, misp_category): - """Make a new transition due to file attribute event. + @abc.abstractmethod + def __file_transition(self, graph, node, misp_category): + """Make a new transition due to file attribute event. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. - misp_category: (str): MISP category of the given node. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. - Returns: - MispEventRule: the transited rule. - """ - pass + Returns: + MispEventRule: the transited rule. + """ + pass - @abc.abstractmethod - def __ip_transition(self, graph, node, misp_category): - """Make a new transition due to ip attribute event. + @abc.abstractmethod + def __ip_transition(self, graph, node, misp_category): + """Make a new transition due to ip attribute event. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. - misp_category: (str): MISP category of the given node. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. - Returns: - MispEventRule: the transited rule. - """ - pass + Returns: + MispEventRule: the transited rule. + """ + pass - @abc.abstractmethod - def __url_transition(self, graph, node, misp_category): - """Make a new transition due to url attribute event. + @abc.abstractmethod + def __url_transition(self, graph, node, misp_category): + """Make a new transition due to url attribute event. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. - misp_category: (str): MISP category of the given node. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. - Returns: - MispEventRule: the transited rule. - """ - pass + Returns: + MispEventRule: the transited rule. + """ + pass - @abc.abstractmethod - def __domain_transition(self, graph, node, misp_category): - """Make a new transition due to domain attribute event. + @abc.abstractmethod + def __domain_transition(self, graph, node, misp_category): + """Make a new transition due to domain attribute event. - Args: - graph (VTGraph): graph to be computed. - node (Node): the node to be linked. - misp_category: (str): MISP category of the given node. + Args: + graph (VTGraph): graph to be computed. + node (Node): the node to be linked. + misp_category: (str): MISP category of the given node. - Returns: - MispEventRule: the transited rule. - """ - pass + Returns: + MispEventRule: the transited rule. + """ + pass class MispEventURLRule(MispEventRule): - """Rule for URL event.""" + """Rule for URL event.""" - def __init__(self, last_rule=None, node=None): - super(MispEventURLRule, self).__init__(last_rule, node) - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + def __init__(self, last_rule=None, node=None): + super(MispEventURLRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def __file_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "downloaded_files") - return MispEventFileRule(self, node) + def __file_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "downloaded_files") + return MispEventFileRule(self, node) - def __ip_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "contacted_ips") - return MispEventIPRule(self, node) + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_ips") + return MispEventIPRule(self, node) - def __url_transition(self, graph, node, misp_category): - suitable_rule = self.get_last_different_rule() - if not isinstance(suitable_rule, MispEventInitialRule): - return suitable_rule.resolve_relation(graph, node, misp_category) - else: - return MispEventURLRule(self, node) + def __url_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventURLRule(self, node) - def __domain_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "contacted_domains") - return MispEventDomainRule(self, node) + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_domains") + return MispEventDomainRule(self, node) class MispEventIPRule(MispEventRule): - """Rule for IP event.""" + """Rule for IP event.""" - def __init__(self, last_rule=None, node=None): - super(MispEventIPRule, self).__init__(last_rule, node) - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + def __init__(self, last_rule=None, node=None): + super(MispEventIPRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def __file_transition(self, graph, node, misp_category): - connection_type = "communicating_files" - if misp_category == "Artifacts dropped": - connection_type = "downloaded_files" - graph.add_link(self.node.node_id, node.node_id, connection_type) - return MispEventFileRule(self, node) + def __file_transition(self, graph, node, misp_category): + connection_type = "communicating_files" + if misp_category == "Artifacts dropped": + connection_type = "downloaded_files" + graph.add_link(self.node.node_id, node.node_id, connection_type) + return MispEventFileRule(self, node) - def __ip_transition(self, graph, node, misp_category): - suitable_rule = self.get_last_different_rule() - if not isinstance(suitable_rule, MispEventInitialRule): - return suitable_rule.resolve_relation(graph, node, misp_category) - else: - return MispEventIPRule(self, node) + def __ip_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventIPRule(self, node) - def __url_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "urls") - return MispEventURLRule(self, node) + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "urls") + return MispEventURLRule(self, node) - def __domain_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "resolutions") - return MispEventDomainRule(self, node) + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "resolutions") + return MispEventDomainRule(self, node) class MispEventDomainRule(MispEventRule): - """Rule for domain event.""" + """Rule for domain event.""" - def __init__(self, last_rule=None, node=None): - super(MispEventDomainRule, self).__init__(last_rule, node) - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + def __init__(self, last_rule=None, node=None): + super(MispEventDomainRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def __file_transition(self, graph, node, misp_category): - connection_type = "communicating_files" - if misp_category == "Artifacts dropped": - connection_type = "downloaded_files" - graph.add_link(self.node.node_id, node.node_id, connection_type) - return MispEventFileRule(self, node) + def __file_transition(self, graph, node, misp_category): + connection_type = "communicating_files" + if misp_category == "Artifacts dropped": + connection_type = "downloaded_files" + graph.add_link(self.node.node_id, node.node_id, connection_type) + return MispEventFileRule(self, node) - def __ip_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "resolutions") - return MispEventIPRule(self, node) + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "resolutions") + return MispEventIPRule(self, node) - def __url_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "urls") - return MispEventURLRule(self, node) + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "urls") + return MispEventURLRule(self, node) - def __domain_transition(self, graph, node, misp_category): - suitable_rule = self.get_last_different_rule() - if not isinstance(suitable_rule, MispEventInitialRule): - return suitable_rule.resolve_relation(graph, node, misp_category) - else: - graph.add_link(self.node.node_id, node.node_id, "siblings") - return MispEventDomainRule(self, node) + def __domain_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + graph.add_link(self.node.node_id, node.node_id, "siblings") + return MispEventDomainRule(self, node) class MispEventFileRule(MispEventRule): - """Rule for File event.""" + """Rule for File event.""" - def __init__(self, last_rule=None, node=None): - super(MispEventFileRule, self).__init__(last_rule, node) - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + def __init__(self, last_rule=None, node=None): + super(MispEventFileRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def __file_transition(self, graph, node, misp_category): - suitable_rule = self.get_last_different_rule() - if not isinstance(suitable_rule, MispEventInitialRule): - return suitable_rule.resolve_relation(graph, node, misp_category) - else: - return MispEventFileRule(self, node) + def __file_transition(self, graph, node, misp_category): + suitable_rule = self.get_last_different_rule() + if not isinstance(suitable_rule, MispEventInitialRule): + return suitable_rule.resolve_relation(graph, node, misp_category) + else: + return MispEventFileRule(self, node) - def __ip_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "contacted_ips") - return MispEventIPRule(self, node) + def __ip_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_ips") + return MispEventIPRule(self, node) - def __url_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "contacted_urls") - return MispEventURLRule(self, node) + def __url_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_urls") + return MispEventURLRule(self, node) - def __domain_transition(self, graph, node, misp_category): - graph.add_link(self.node.node_id, node.node_id, "contacted_domains") - return MispEventDomainRule(self, node) + def __domain_transition(self, graph, node, misp_category): + graph.add_link(self.node.node_id, node.node_id, "contacted_domains") + return MispEventDomainRule(self, node) class MispEventInitialRule(MispEventRule): - """Initial rule.""" + """Initial rule.""" - def __init__(self, last_rule=None, node=None): - super(MispEventInitialRule, self).__init__(last_rule, node) - self.relation_event = { - "ip_address": self.__ip_transition, - "url": self.__url_transition, - "domain": self.__domain_transition, - "file": self.__file_transition - } + def __init__(self, last_rule=None, node=None): + super(MispEventInitialRule, self).__init__(last_rule, node) + self.relation_event = { + "ip_address": self.__ip_transition, + "url": self.__url_transition, + "domain": self.__domain_transition, + "file": self.__file_transition + } - def __file_transition(self, graph, node, misp_category): - return MispEventFileRule(self, node) + def __file_transition(self, graph, node, misp_category): + return MispEventFileRule(self, node) - def __ip_transition(self, graph, node, misp_category): - return MispEventIPRule(self, node) + def __ip_transition(self, graph, node, misp_category): + return MispEventIPRule(self, node) - def __url_transition(self, graph, node, misp_category): - return MispEventURLRule(self, node) + def __url_transition(self, graph, node, misp_category): + return MispEventURLRule(self, node) - def __domain_transition(self, graph, node, misp_category): - return MispEventDomainRule(self, node) + def __domain_transition(self, graph, node, misp_category): + return MispEventDomainRule(self, node) diff --git a/misp_modules/lib/vt_graph_parser/helpers/wrappers.py b/misp_modules/lib/vt_graph_parser/helpers/wrappers.py index 8735317..d376d43 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/wrappers.py +++ b/misp_modules/lib/vt_graph_parser/helpers/wrappers.py @@ -5,55 +5,54 @@ This module provides a Python object wrapper for MISP objects. class MispAttribute(object): - """Python object wrapper for MISP attribute. + """Python object wrapper for MISP attribute. - Attributes: - type (str): VirusTotal node type. - category (str): MISP attribute category. - value (str): node id. - label (str): node name. - misp_type (str): MISP node type. - """ - - MISP_TYPES_REFERENCE = { - "hostname": "domain", - "domain": "domain", - "ip-src": "ip_address", - "ip-dst": "ip_address", - "url": "url", - "filename|X": "file", - "filename": "file", - "md5": "file", - "sha1": "file", - "sha256": "file", - "target-user": "victim", - "target-email": "email" - } - - def __init__(self, misp_type, category, value, label=""): - """Constructor for a MispAttribute. - - Args: - misp_type (str): MISP type attribute. - category (str): MISP category attribute. - value (str): attribute value. - label (str): attribute label. + Attributes: + type (str): VirusTotal node type. + category (str): MISP attribute category. + value (str): node id. + label (str): node name. + misp_type (str): MISP node type. """ - if misp_type.startswith("filename|"): - label, value = value.split("|") - misp_type = "filename|X" - if misp_type == "filename": - label = value - self.type = self.MISP_TYPES_REFERENCE.get(misp_type) - self.category = category - self.value = value - self.label = label - self.misp_type = misp_type + MISP_TYPES_REFERENCE = { + "hostname": "domain", + "domain": "domain", + "ip-src": "ip_address", + "ip-dst": "ip_address", + "url": "url", + "filename|X": "file", + "filename": "file", + "md5": "file", + "sha1": "file", + "sha256": "file", + "target-user": "victim", + "target-email": "email" + } - def __eq__(self, other): - return (isinstance(other, self.__class__) and self.value == other.value and - self.type == other.type) + def __init__(self, misp_type, category, value, label=""): + """Constructor for a MispAttribute. - def __repr__(self): - return 'MispAttribute("{type}", "{category}", "{value}")'.format(type=self.type, category=self.category, value=self.value) + Args: + misp_type (str): MISP type attribute. + category (str): MISP category attribute. + value (str): attribute value. + label (str): attribute label. + """ + if misp_type.startswith("filename|"): + label, value = value.split("|") + misp_type = "filename|X" + if misp_type == "filename": + label = value + + self.type = self.MISP_TYPES_REFERENCE.get(misp_type) + self.category = category + self.value = value + self.label = label + self.misp_type = misp_type + + def __eq__(self, other): + return (isinstance(other, self.__class__) and self.value == other.value and self.type == other.type) + + def __repr__(self): + return 'MispAttribute("{type}", "{category}", "{value}")'.format(type=self.type, category=self.category, value=self.value) diff --git a/misp_modules/lib/vt_graph_parser/importers/base.py b/misp_modules/lib/vt_graph_parser/importers/base.py index 3cd0192..4d9b855 100644 --- a/misp_modules/lib/vt_graph_parser/importers/base.py +++ b/misp_modules/lib/vt_graph_parser/importers/base.py @@ -9,90 +9,90 @@ from lib.vt_graph_parser.helpers.rules import MispEventInitialRule def import_misp_graph( - misp_attributes, graph_id, vt_api_key, fetch_information, name, - private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, - group_viewers, use_vt_to_connect_the_graph, max_api_quotas, - max_search_depth): - """Import VirusTotal Graph from MISP. + misp_attributes, graph_id, vt_api_key, fetch_information, name, + private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, + group_viewers, use_vt_to_connect_the_graph, max_api_quotas, + max_search_depth): + """Import VirusTotal Graph from MISP. - Args: - misp_attributes ([MispAttribute]): list with the MISP attributes which - will be added to the returned graph. - graph_id: if supplied, the graph will be loaded instead of compute it again. - vt_api_key (str): VT API Key. - fetch_information (bool): whether the script will fetch - information for added nodes in VT. Defaults to True. - name (str): graph title. Defaults to "". - private (bool): True for private graphs. You need to have - Private Graph premium features enabled in your subscription. Defaults - to False. - fetch_vt_enterprise (bool, optional): if True, the graph will search any - available information using VirusTotal Intelligence for the node if there - is no normal information for it. Defaults to False. - user_editors ([str]): usernames that can edit the graph. - Defaults to None. - user_viewers ([str]): usernames that can view the graph. - Defaults to None. - group_editors ([str]): groups that can edit the graph. - Defaults to None. - group_viewers ([str]): groups that can view the graph. - Defaults to None. - use_vt_to_connect_the_graph (bool): if True, graph nodes will - be linked using VirusTotal API. Otherwise, the links will be generated - using production rules based on MISP attributes order. Defaults to - False. - max_api_quotas (int): maximum number of api quotas that could - be consumed to resolve graph using VirusTotal API. Defaults to 20000. - max_search_depth (int, optional): max search depth to explore - relationship between nodes when use_vt_to_connect_the_graph is True. - Defaults to 3. + Args: + misp_attributes ([MispAttribute]): list with the MISP attributes which + will be added to the returned graph. + graph_id: if supplied, the graph will be loaded instead of compute it again. + vt_api_key (str): VT API Key. + fetch_information (bool): whether the script will fetch + information for added nodes in VT. Defaults to True. + name (str): graph title. Defaults to "". + private (bool): True for private graphs. You need to have + Private Graph premium features enabled in your subscription. Defaults + to False. + fetch_vt_enterprise (bool, optional): if True, the graph will search any + available information using VirusTotal Intelligence for the node if there + is no normal information for it. Defaults to False. + user_editors ([str]): usernames that can edit the graph. + Defaults to None. + user_viewers ([str]): usernames that can view the graph. + Defaults to None. + group_editors ([str]): groups that can edit the graph. + Defaults to None. + group_viewers ([str]): groups that can view the graph. + Defaults to None. + use_vt_to_connect_the_graph (bool): if True, graph nodes will + be linked using VirusTotal API. Otherwise, the links will be generated + using production rules based on MISP attributes order. Defaults to + False. + max_api_quotas (int): maximum number of api quotas that could + be consumed to resolve graph using VirusTotal API. Defaults to 20000. + max_search_depth (int, optional): max search depth to explore + relationship between nodes when use_vt_to_connect_the_graph is True. + Defaults to 3. - If use_vt_to_connect_the_graph is True, it will take some time to compute - graph. + If use_vt_to_connect_the_graph is True, it will take some time to compute + graph. - Returns: - vt_graph_api.graph.VTGraph: the imported graph. - """ + Returns: + vt_graph_api.graph.VTGraph: the imported graph. + """ - rule = MispEventInitialRule() + rule = MispEventInitialRule() - # Check if the event has been already computed in VirusTotal Graph. Otherwise - # a new graph will be created. - if not graph_id: - graph = vt_graph_api.VTGraph( - api_key=vt_api_key, name=name, private=private, - user_editors=user_editors, user_viewers=user_viewers, - group_editors=group_editors, group_viewers=group_viewers) - else: - graph = vt_graph_api.VTGraph.load_graph(graph_id, vt_api_key) - - attributes_to_add = [attr for attr in misp_attributes - if not graph.has_node(attr.value)] - - total_expandable_attrs = max(sum( - 1 for attr in attributes_to_add - if attr.type in vt_graph_api.Node.SUPPORTED_NODE_TYPES), - 1) - - max_quotas_per_search = max(int(max_api_quotas / total_expandable_attrs), 1) - - previous_node_id = "" - for attr in attributes_to_add: - # Add the current attr as node to the graph. - added_node = graph.add_node( - attr.value, attr.type, fetch_information, fetch_vt_enterprise, - attr.label) - # If use_vt_to_connect_the_grap is True the nodes will be connected using - # VT API. - if use_vt_to_connect_the_graph: - if (attr.type not in vt_graph_api.Node.SUPPORTED_NODE_TYPES and - previous_node_id): - graph.add_link(previous_node_id, attr.value, "manual") - else: - graph.connect_with_graph( - attr.value, max_quotas_per_search, max_search_depth, - fetch_info_collected_nodes=fetch_information) + # Check if the event has been already computed in VirusTotal Graph. Otherwise + # a new graph will be created. + if not graph_id: + graph = vt_graph_api.VTGraph( + api_key=vt_api_key, name=name, private=private, + user_editors=user_editors, user_viewers=user_viewers, + group_editors=group_editors, group_viewers=group_viewers) else: - rule = rule.resolve_relation(graph, added_node, attr.category) + graph = vt_graph_api.VTGraph.load_graph(graph_id, vt_api_key) - return graph + attributes_to_add = [attr for attr in misp_attributes + if not graph.has_node(attr.value)] + + total_expandable_attrs = max(sum( + 1 for attr in attributes_to_add + if attr.type in vt_graph_api.Node.SUPPORTED_NODE_TYPES), + 1) + + max_quotas_per_search = max( + int(max_api_quotas / total_expandable_attrs), 1) + + previous_node_id = "" + for attr in attributes_to_add: + # Add the current attr as node to the graph. + added_node = graph.add_node( + attr.value, attr.type, fetch_information, fetch_vt_enterprise, + attr.label) + # If use_vt_to_connect_the_grap is True the nodes will be connected using + # VT API. + if use_vt_to_connect_the_graph: + if (attr.type not in vt_graph_api.Node.SUPPORTED_NODE_TYPES and previous_node_id): + graph.add_link(previous_node_id, attr.value, "manual") + else: + graph.connect_with_graph( + attr.value, max_quotas_per_search, max_search_depth, + fetch_info_collected_nodes=fetch_information) + else: + rule = rule.resolve_relation(graph, added_node, attr.category) + + return graph diff --git a/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py index c01b6a1..86a3b25 100644 --- a/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +++ b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py @@ -5,71 +5,69 @@ response payload giving by MISP API directly. """ -import json -from lib.vt_graph_parser import errors from lib.vt_graph_parser.helpers.parsers import parse_pymisp_response from lib.vt_graph_parser.importers.base import import_misp_graph def from_pymisp_response( - payload, vt_api_key, fetch_information=True, - private=False, fetch_vt_enterprise=False, user_editors=None, - user_viewers=None, group_editors=None, group_viewers=None, - use_vt_to_connect_the_graph=False, max_api_quotas=1000, - max_search_depth=3, expand_node_one_level=False): - """Import VirusTotal Graph from MISP JSON file. + payload, vt_api_key, fetch_information=True, + private=False, fetch_vt_enterprise=False, user_editors=None, + user_viewers=None, group_editors=None, group_viewers=None, + use_vt_to_connect_the_graph=False, max_api_quotas=1000, + max_search_depth=3, expand_node_one_level=False): + """Import VirusTotal Graph from MISP JSON file. - Args: - payload (dict): dictionary which contains the request payload. - vt_api_key (str): VT API Key. - fetch_information (bool, optional): whether the script will fetch - information for added nodes in VT. Defaults to True. - name (str, optional): graph title. Defaults to "". - private (bool, optional): True for private graphs. You need to have - Private Graph premium features enabled in your subscription. Defaults - to False. - fetch_vt_enterprise (bool, optional): if True, the graph will search any - available information using VirusTotal Intelligence for the node if there - is no normal information for it. Defaults to False. - user_editors ([str], optional): usernames that can edit the graph. - Defaults to None. - user_viewers ([str], optional): usernames that can view the graph. - Defaults to None. - group_editors ([str], optional): groups that can edit the graph. - Defaults to None. - group_viewers ([str], optional): groups that can view the graph. - Defaults to None. - use_vt_to_connect_the_graph (bool, optional): if True, graph nodes will - be linked using VirusTotal API. Otherwise, the links will be generated - using production rules based on MISP attributes order. Defaults to - False. - max_api_quotas (int, optional): maximum number of api quotas that could - be consumed to resolve graph using VirusTotal API. Defaults to 20000. - max_search_depth (int, optional): max search depth to explore - relationship between nodes when use_vt_to_connect_the_graph is True. - Defaults to 3. - expand_one_level (bool, optional): expand entire graph one level. - Defaults to False. + Args: + payload (dict): dictionary which contains the request payload. + vt_api_key (str): VT API Key. + fetch_information (bool, optional): whether the script will fetch + information for added nodes in VT. Defaults to True. + name (str, optional): graph title. Defaults to "". + private (bool, optional): True for private graphs. You need to have + Private Graph premium features enabled in your subscription. Defaults + to False. + fetch_vt_enterprise (bool, optional): if True, the graph will search any + available information using VirusTotal Intelligence for the node if there + is no normal information for it. Defaults to False. + user_editors ([str], optional): usernames that can edit the graph. + Defaults to None. + user_viewers ([str], optional): usernames that can view the graph. + Defaults to None. + group_editors ([str], optional): groups that can edit the graph. + Defaults to None. + group_viewers ([str], optional): groups that can view the graph. + Defaults to None. + use_vt_to_connect_the_graph (bool, optional): if True, graph nodes will + be linked using VirusTotal API. Otherwise, the links will be generated + using production rules based on MISP attributes order. Defaults to + False. + max_api_quotas (int, optional): maximum number of api quotas that could + be consumed to resolve graph using VirusTotal API. Defaults to 20000. + max_search_depth (int, optional): max search depth to explore + relationship between nodes when use_vt_to_connect_the_graph is True. + Defaults to 3. + expand_one_level (bool, optional): expand entire graph one level. + Defaults to False. - If use_vt_to_connect_the_graph is True, it will take some time to compute - graph. + If use_vt_to_connect_the_graph is True, it will take some time to compute + graph. - Raises: - LoaderError: if JSON file is invalid. + Raises: + LoaderError: if JSON file is invalid. - Returns: - [vt_graph_api.graph.VTGraph: the imported graph]. - """ - graphs = [] - for event_payload in payload['data']: - misp_attrs, graph_id = parse_pymisp_response(event_payload) - name = "Graph created from MISP event" - graph = import_misp_graph( - misp_attrs, graph_id, vt_api_key, fetch_information, name, - private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, - group_viewers, use_vt_to_connect_the_graph, max_api_quotas, - max_search_depth) - if expand_node_one_level: - graph.expand_n_level(1) - graphs.append(graph) - return graphs + Returns: + [vt_graph_api.graph.VTGraph: the imported graph]. + """ + graphs = [] + for event_payload in payload['data']: + misp_attrs, graph_id = parse_pymisp_response(event_payload) + name = "Graph created from MISP event" + graph = import_misp_graph( + misp_attrs, graph_id, vt_api_key, fetch_information, name, + private, fetch_vt_enterprise, user_editors, user_viewers, group_editors, + group_viewers, use_vt_to_connect_the_graph, max_api_quotas, + max_search_depth) + if expand_node_one_level: + graph.expand_n_level(1) + graphs.append(graph) + return graphs diff --git a/misp_modules/modules/export_mod/vt_graph.py b/misp_modules/modules/export_mod/vt_graph.py index 9d20a00..d8b3359 100644 --- a/misp_modules/modules/export_mod/vt_graph.py +++ b/misp_modules/modules/export_mod/vt_graph.py @@ -43,71 +43,71 @@ moduleconfig = [ def handler(q=False): - """Expansion handler. + """Expansion handler. - Args: - q (bool, optional): module data. Defaults to False. + Args: + q (bool, optional): module data. Defaults to False. - Returns: - [str]: VirusTotal graph links - """ - if not q: - return False - request = json.loads(q) + Returns: + [str]: VirusTotal graph links + """ + if not q: + return False + request = json.loads(q) - if not request.get('config') or not request['config'].get('vt_api_key'): - misperrors['error'] = 'A VirusTotal api key is required for this module.' - return misperrors + if not request.get('config') or not request['config'].get('vt_api_key'): + misperrors['error'] = 'A VirusTotal api key is required for this module.' + return misperrors - config = request['config'] + config = request['config'] - api_key = config.get('vt_api_key') - fetch_information = config.get('fetch_information') or False - private = config.get('private') or False - fetch_vt_enterprise = config.get('fetch_vt_enterprise') or False - expand_one_level = config.get('expand_one_level') or False + api_key = config.get('vt_api_key') + fetch_information = config.get('fetch_information') or False + private = config.get('private') or False + fetch_vt_enterprise = config.get('fetch_vt_enterprise') or False + expand_one_level = config.get('expand_one_level') or False - user_editors = config.get('user_editors') - if user_editors: - user_editors = user_editors.split(',') - user_viewers = config.get('user_viewers') - if user_viewers: - user_viewers = user_viewers.split(',') - group_editors = config.get('group_editors') - if group_editors: - group_editors = group_editors.split(',') - group_viewers = config.get('group_viewers') - if group_viewers: - group_viewers = group_viewers.split(',') - + user_editors = config.get('user_editors') + if user_editors: + user_editors = user_editors.split(',') + user_viewers = config.get('user_viewers') + if user_viewers: + user_viewers = user_viewers.split(',') + group_editors = config.get('group_editors') + if group_editors: + group_editors = group_editors.split(',') + group_viewers = config.get('group_viewers') + if group_viewers: + group_viewers = group_viewers.split(',') - graphs = from_pymisp_response( - request, api_key, fetch_information=fetch_information, - private=private, fetch_vt_enterprise=fetch_vt_enterprise, - user_editors=user_editors, user_viewers=user_viewers, - group_editors=group_editors, group_viewers=group_viewers, - expand_node_one_level=expand_one_level) - links = [] + graphs = from_pymisp_response( + request, api_key, fetch_information=fetch_information, + private=private, fetch_vt_enterprise=fetch_vt_enterprise, + user_editors=user_editors, user_viewers=user_viewers, + group_editors=group_editors, group_viewers=group_viewers, + expand_node_one_level=expand_one_level) + links = [] - for graph in graphs: - graph.save_graph() - links.append(graph.get_ui_link()) + for graph in graphs: + graph.save_graph() + links.append(graph.get_ui_link()) - # This file will contains one VirusTotal graph link for each exported event - file_data = str(base64.b64encode(bytes('\n'.join(links), 'utf-8')), 'utf-8') - return {'response': [], 'data': file_data} + # This file will contains one VirusTotal graph link for each exported event + file_data = str(base64.b64encode( + bytes('\n'.join(links), 'utf-8')), 'utf-8') + return {'response': [], 'data': file_data} def introspection(): - modulesetup = { - 'responseType': 'application/txt', - 'outputFileExtension': 'txt', - 'userConfig': {}, - 'inputSource': [] - } - return modulesetup + modulesetup = { + 'responseType': 'application/txt', + 'outputFileExtension': 'txt', + 'userConfig': {}, + 'inputSource': [] + } + return modulesetup def version(): - moduleinfo['config'] = moduleconfig - return moduleinfo + moduleinfo['config'] = moduleconfig + return moduleinfo From f197abdcf6e719671bb62bc0643eb2f9f65c4108 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 9 Jan 2020 16:04:29 +0100 Subject: [PATCH 072/220] chg: Bumped pipfile.lock with up-to-date libraries and new vt_graph_api library requirement --- Pipfile.lock | 342 ++++++++++++++++++++++++++------------------------- 1 file changed, 175 insertions(+), 167 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index dab4860..b977ce7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "30e84f4986146c248e706f52f425649660225889bfcdf5075c99854442ae5f42" + "sha256": "b62db6df8a7b42f4c6915d6fbb1d4c38ccbb7209e559708433d28cdddebd3df9" }, "pipfile-spec": 6, "requires": { @@ -96,12 +96,12 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:5279c36b4b2ec2cb4298d723791467e3000e5384a43ea0cdf5d45207c7e97169", - "sha256:6135db2ba678168c07950f9a16c4031822c6f4aec75a65e0a97bc5ca09789931", - "sha256:dcdef580e18a76d54002088602eba453eec38ebbcafafeaabd8cab12b6155d57" + "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", + "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", + "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae" ], "index": "pypi", - "version": "==4.8.1" + "version": "==4.8.2" }, "blockchain": { "hashes": [ @@ -264,20 +264,28 @@ ], "version": "==0.18.2" }, + "futures": { + "hashes": [ + "sha256:3a44f286998ae64f0cc083682fcfec16c406134a81a589a5de445d7bb7c2751b", + "sha256:51ecb45f0add83c806c68e4b06106f90db260585b25ef2abfcda0bd95c0132fd", + "sha256:c4884a65654a7c45435063e14ae85280eb1f111d94e542396717ba9828c4337f" + ], + "version": "==3.1.1" + }, "geoip2": { "hashes": [ - "sha256:a37ddac2d200ffb97c736da8b8ba9d5d8dc47da6ec0f162a461b681ecac53a14", - "sha256:f7ffe9d258e71a42cf622ce6350d976de1d0312b9f2fbce3975c7d838b57ecf0" + "sha256:5869e987bc54c0d707264fec4710661332cc38d2dca5a7f9bb5362d0308e2ce0", + "sha256:99ec12d2f1271a73a0a4a2b663fe6ce25fd02289c0a6bef05c0a1c3b30ee95a4" ], "index": "pypi", - "version": "==2.9.0" + "version": "==3.0.0" }, "httplib2": { "hashes": [ - "sha256:34537dcdd5e0f2386d29e0e2c6d4a1703a3b982d34c198a5102e6e5d6194b107", - "sha256:409fa5509298f739b34d5a652df762cb0042507dc93f6633e306b11289d6249d" + "sha256:1d1f4ad7a6e55d325830ab274190f98894e069850a871fac19921caf4363259d", + "sha256:a5f914f18f99cb9541660454a159e3b3c63241fc3ab60005bb88d97cc7a4fb58" ], - "version": "==0.14.0" + "version": "==0.15.0" }, "idna": { "hashes": [ @@ -384,9 +392,9 @@ }, "maxminddb": { "hashes": [ - "sha256:449a1713d37320d777d0db286286ab22890f0a176492ecf3ad8d9319108f2f79" + "sha256:d0ce131d901eb11669996b49a59f410efd3da2c6dbe2c0094fe2fef8d85b6336" ], - "version": "==1.5.1" + "version": "==1.5.2" }, "misp-modules": { "editable": true, @@ -401,25 +409,25 @@ }, "multidict": { "hashes": [ - "sha256:09c19f642e055550c9319d5123221b7e07fc79bda58122aa93910e52f2ab2f29", - "sha256:0c1a5d5f7aa7189f7b83c4411c2af8f1d38d69c4360d5de3eea129c65d8d7ce2", - "sha256:12f22980e7ed0972a969520fb1e55682c9fca89a68b21b49ec43132e680be812", - "sha256:258660e9d6b52de1a75097944e12718d3aa59adc611b703361e3577d69167aaf", - "sha256:3374a23e707848f27b3438500db0c69eca82929337656fce556bd70031fbda74", - "sha256:503b7fce0054c73aa631cc910a470052df33d599f3401f3b77e54d31182525d5", - "sha256:6ce55f2c45ffc90239aab625bb1b4864eef33f73ea88487ef968291fbf09fb3f", - "sha256:725496dde5730f4ad0a627e1a58e2620c1bde0ad1c8080aae15d583eb23344ce", - "sha256:a3721078beff247d0cd4fb19d915c2c25f90907cf8d6cd49d0413a24915577c6", - "sha256:ba566518550f81daca649eded8b5c7dd09210a854637c82351410aa15c49324a", - "sha256:c42362750a51a15dc905cb891658f822ee5021bfbea898c03aa1ed833e2248a5", - "sha256:cf14aaf2ab067ca10bca0b14d5cbd751dd249e65d371734bc0e47ddd8fafc175", - "sha256:cf24e15986762f0e75a622eb19cfe39a042e952b8afba3e7408835b9af2be4fb", - "sha256:d7b6da08538302c5245cd3103f333655ba7f274915f1f5121c4f4b5fbdb3febe", - "sha256:e27e13b9ff0a914a6b8fb7e4947d4ac6be8e4f61ede17edffabd088817df9e26", - "sha256:e53b205f8afd76fc6c942ef39e8ee7c519c775d336291d32874082a87802c67c", - "sha256:ec804fc5f68695d91c24d716020278fcffd50890492690a7e1fef2e741f7172c" + "sha256:0f04bf4c15d8417401a10a650c349ccc0285943681bfd87d3690587d7714a9b4", + "sha256:15a61c0df2d32487e06f6084eabb48fd9e8b848315e397781a70caf9670c9d78", + "sha256:3c5e2dcbe6b04cbb4303e47a896757a77b676c5e5db5528be7ff92f97ba7ab95", + "sha256:5d2b32b890d9e933d3ced417924261802a857abdee9507b68c75014482145c03", + "sha256:5e5fb8bfebf87f2e210306bf9dd8de2f1af6782b8b78e814060ae9254ab1f297", + "sha256:63ba2be08d82ea2aa8b0f7942a74af4908664d26cb4ff60c58eadb1e33e7da00", + "sha256:73740fcdb38f0adcec85e97db7557615b50ec4e5a3e73e35878720bcee963382", + "sha256:78bed18e7f1eb21f3d10ff3acde900b4d630098648fe1d65bb4abfb3e22c4900", + "sha256:a02fade7b5476c4f88efe9593ff2f3286698d8c6d715ba4f426954f73f382026", + "sha256:aacbde3a8875352a640efa2d1b96e5244a29b0f8df79cbf1ec6470e86fd84697", + "sha256:be813fb9e5ce41a5a99a29cdb857144a1bd6670883586f995b940a4878dc5238", + "sha256:bfcad6da0b8839f01a819602aaa5c5a5b4c85ecbfae9b261a31df3d9262fb31e", + "sha256:c2bfc0db3166e68515bc4a2b9164f4f75ae9c793e9635f8651f2c9ffc65c8dad", + "sha256:c66d11870ae066499a3541963e6ce18512ca827c2aaeaa2f4e37501cee39ac5d", + "sha256:cc7f2202b753f880c2e4123f9aacfdb94560ba893e692d24af271dac41f8b8d9", + "sha256:d1f45e5bb126662ba66ee579831ce8837b1fd978115c9657e32eb3c75b92973d", + "sha256:ed5f3378c102257df9e2dc9ce6468dabf68bee9ec34969cfdc472631aba00316" ], - "version": "==4.7.1" + "version": "==4.7.3" }, "np": { "hashes": [ @@ -430,29 +438,29 @@ }, "numpy": { "hashes": [ - "sha256:0a7a1dd123aecc9f0076934288ceed7fd9a81ba3919f11a855a7887cbe82a02f", - "sha256:0c0763787133dfeec19904c22c7e358b231c87ba3206b211652f8cbe1241deb6", - "sha256:3d52298d0be333583739f1aec9026f3b09fdfe3ddf7c7028cb16d9d2af1cca7e", - "sha256:43bb4b70585f1c2d153e45323a886839f98af8bfa810f7014b20be714c37c447", - "sha256:475963c5b9e116c38ad7347e154e5651d05a2286d86455671f5b1eebba5feb76", - "sha256:64874913367f18eb3013b16123c9fed113962e75d809fca5b78ebfbb73ed93ba", - "sha256:683828e50c339fc9e68720396f2de14253992c495fdddef77a1e17de55f1decc", - "sha256:6ca4000c4a6f95a78c33c7dadbb9495c10880be9c89316aa536eac359ab820ae", - "sha256:75fd817b7061f6378e4659dd792c84c0b60533e867f83e0d1e52d5d8e53df88c", - "sha256:7d81d784bdbed30137aca242ab307f3e65c8d93f4c7b7d8f322110b2e90177f9", - "sha256:8d0af8d3664f142414fd5b15cabfd3b6cc3ef242a3c7a7493257025be5a6955f", - "sha256:9679831005fb16c6df3dd35d17aa31dc0d4d7573d84f0b44cc481490a65c7725", - "sha256:a8f67ebfae9f575d85fa859b54d3bdecaeece74e3274b0b5c5f804d7ca789fe1", - "sha256:acbf5c52db4adb366c064d0b7c7899e3e778d89db585feadd23b06b587d64761", - "sha256:ada4805ed51f5bcaa3a06d3dd94939351869c095e30a2b54264f5a5004b52170", - "sha256:c7354e8f0eca5c110b7e978034cd86ed98a7a5ffcf69ca97535445a595e07b8e", - "sha256:e2e9d8c87120ba2c591f60e32736b82b67f72c37ba88a4c23c81b5b8fa49c018", - "sha256:e467c57121fe1b78a8f68dd9255fbb3bb3f4f7547c6b9e109f31d14569f490c3", - "sha256:ede47b98de79565fcd7f2decb475e2dcc85ee4097743e551fe26cfc7eb3ff143", - "sha256:f58913e9227400f1395c7b800503ebfdb0772f1c33ff8cb4d6451c06cabdf316", - "sha256:fe39f5fd4103ec4ca3cb8600b19216cd1ff316b4990f4c0b6057ad982c0a34d5" + "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", + "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e", + "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc", + "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc", + "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a", + "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa", + "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3", + "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121", + "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971", + "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26", + "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd", + "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480", + "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec", + "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77", + "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57", + "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07", + "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572", + "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73", + "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca", + "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474", + "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5" ], - "version": "==1.17.4" + "version": "==1.18.1" }, "oauth2": { "hashes": [ @@ -543,46 +551,38 @@ }, "pdftotext": { "hashes": [ - "sha256:c8bdc47b08baa17b8e03ba1f960fc6335b183d2644eaf7300e088516758a6090" + "sha256:b56f6ff1a564803ab8d849b3bb350b27087c15f5fe4e542a6370645543b0adf9" ], "index": "pypi", - "version": "==2.1.2" + "version": "==2.1.3" }, "pillow": { "hashes": [ - "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", - "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", - "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", - "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", - "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", - "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", - "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", - "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", - "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", - "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", - "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", - "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", - "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", - "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", - "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", - "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", - "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", - "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", - "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", - "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", - "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", - "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", - "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", - "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", - "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", - "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", - "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", - "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", - "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", - "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" + "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be", + "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946", + "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837", + "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f", + "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00", + "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d", + "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533", + "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a", + "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358", + "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda", + "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435", + "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2", + "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313", + "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff", + "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317", + "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2", + "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614", + "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0", + "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386", + "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9", + "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636", + "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865" ], "index": "pypi", - "version": "==6.2.1" + "version": "==7.0.0" }, "progressbar2": { "hashes": [ @@ -736,7 +736,7 @@ "fileobjects,openioc,virustotal,pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "a26a8e450b14d48bb0c8ef46b32bff2f1eadc514" + "ref": "3ee7d8c67601bee658f1c0f488635796e5d7eb04" }, "pyonyphe": { "editable": true, @@ -752,10 +752,10 @@ }, "pyparsing": { "hashes": [ - "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", - "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" ], - "version": "==2.4.5" + "version": "==2.4.6" }, "pypdns": { "hashes": [ @@ -774,16 +774,16 @@ }, "pyrsistent": { "hashes": [ - "sha256:f3b280d030afb652f79d67c5586157c5c1355c9a58dfc7940566e28d28f3df1b" + "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280" ], - "version": "==0.15.6" + "version": "==0.15.7" }, "pytesseract": { "hashes": [ - "sha256:ae1dce01413d1f8eb0614fd65d831e26e649dc1a31699b7275455c57aa563b59" + "sha256:03735b242439f8dbedc0f33ac9d0e980d755d19ed5e51dda1dcd866d9422edc8" ], "index": "pypi", - "version": "==0.3.0" + "version": "==0.3.1" }, "python-dateutil": { "hashes": [ @@ -829,19 +829,19 @@ }, "pyyaml": { "hashes": [ - "sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc", - "sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803", - "sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc", - "sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15", - "sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075", - "sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd", - "sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31", - "sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f", - "sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c", - "sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04", - "sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4" + "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", + "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", + "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", + "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", + "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", + "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", + "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", + "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", + "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", + "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", + "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" ], - "version": "==5.2" + "version": "==5.3" }, "pyzbar": { "hashes": [ @@ -928,10 +928,10 @@ }, "shodan": { "hashes": [ - "sha256:eab999bca9d3b30e6fc549e609194ff2d6fac3caea252414e1d8d735efab8342" + "sha256:ed3c38c749a5d77c935b226b6a7761e972269bd0d55c5c08526af73896aa6edd" ], "index": "pypi", - "version": "==1.21.0" + "version": "==1.21.2" }, "sigmatools": { "hashes": [ @@ -963,12 +963,12 @@ }, "sparqlwrapper": { "hashes": [ - "sha256:14ec551f0d60b4a496ffcc31f15337e844c085b8ead8cbe9a7178748a6de3794", - "sha256:21928e7a97f565e772cdeeb0abad428960f4307e3a13dbdd8f6d3da8a6a506c9", - "sha256:abc3e7eadcad32fa69a85c003853e2f6f73bda6cc999853838f401a5a1ea1109" + "sha256:357ee8a27bc910ea13d77836dbddd0b914991495b8cc1bf70676578155e962a8", + "sha256:c7f9c9d8ebb13428771bc3b6dee54197422507dcc3dea34e30d5dcfc53478dec", + "sha256:d6a66b5b8cda141660e07aeb00472db077a98d22cb588c973209c7336850fb3c" ], "index": "pypi", - "version": "==1.8.4" + "version": "==1.8.5" }, "stix2-patterns": { "hashes": [ @@ -1028,14 +1028,22 @@ ], "version": "==0.14.0" }, - "vulners": { + "vt-graph-api": { "hashes": [ - "sha256:245c07e49e55a604efde43cba723ac7b9345247e5ac8c4f998dcd36c05e4b1b9", - "sha256:82d47d7de208289a746bdb2dd9daf0fadf9fd290618015126091c7d9e2f8a96c", - "sha256:ef0c8e8c4e7d75fbd4d5bb1195109bd7a5b142f60dddc6cea77b3e20a3de1fa8" + "sha256:200c4f5a7c0a518502e890c4f4508a5ea042af9407d2889ef16a17ef11b7d25c", + "sha256:223c1cf32d69e10b5d3e178ec315589c7dfa7d43ccff6630a11ed5c5f498715c" ], "index": "pypi", - "version": "==1.5.4" + "version": "==1.0.1" + }, + "vulners": { + "hashes": [ + "sha256:00ff8744d07f398880afc1efcab6dac4abb614c84553fa31b2d439f986b8e0db", + "sha256:90a855915b4fb4dbd0325643d9e643602975fcb931162e5dc2e7778d1daa2fd8", + "sha256:f230bfcd42663326b7c9b8fa117752e26cad4ccca528caaab531c5b592af8cb5" + ], + "index": "pypi", + "version": "==1.5.5" }, "wand": { "hashes": [ @@ -1047,10 +1055,10 @@ }, "websocket-client": { "hashes": [ - "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9", - "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a" + "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549", + "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010" ], - "version": "==0.56.0" + "version": "==0.57.0" }, "wrapt": { "hashes": [ @@ -1068,10 +1076,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:027fa3d22ccfb5da5d77c29ed740aece286a9a6cc101b564f2f7ca11eb1d490b", - "sha256:5d480cee5babf3865227d5c81269d96be8e87914fc96403ca6fa1b1e4f64c080" + "sha256:18fe8f891a4adf7556c05d56059e136f9fbce5b19f9335f6d7b42c389c4592bc", + "sha256:5d3630ff9b2a277c939bd5053d0e7466499593abebbab9ce1dc9b1481a8ebbb6" ], - "version": "==1.2.6" + "version": "==1.2.7" }, "yara-python": { "hashes": [ @@ -1152,39 +1160,39 @@ }, "coverage": { "hashes": [ - "sha256:0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", - "sha256:1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", - "sha256:2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", - "sha256:3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", - "sha256:44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", - "sha256:475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", - "sha256:47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", - "sha256:4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", - "sha256:50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", - "sha256:56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", - "sha256:79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", - "sha256:79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", - "sha256:7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", - "sha256:81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", - "sha256:81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", - "sha256:8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", - "sha256:88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", - "sha256:91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", - "sha256:979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", - "sha256:9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", - "sha256:a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", - "sha256:b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", - "sha256:ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", - "sha256:be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", - "sha256:c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", - "sha256:c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", - "sha256:d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", - "sha256:d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", - "sha256:e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", - "sha256:e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", - "sha256:fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c" + "sha256:189aac76d6e0d7af15572c51892e7326ee451c076c5a50a9d266406cd6c49708", + "sha256:1bf7ba2af1d373a1750888724f84cffdfc697738f29a353c98195f98fc011509", + "sha256:1f4ee8e2e4243971618bc16fcc4478317405205f135e95226c2496e2a3b8dbbf", + "sha256:225e79a5d485bc1642cb7ba02281419c633c216cdc6b26c26494ba959f09e69f", + "sha256:23688ff75adfa8bfa2a67254d889f9bdf9302c27241d746e17547c42c732d3f4", + "sha256:28f7f73b34a05e23758e860a89a7f649b85c6749e252eff60ebb05532d180e86", + "sha256:2d0cb9b1fe6ad0d915d45ad3d87f03a38e979093a98597e755930db1f897afae", + "sha256:47874b4711c5aeb295c31b228a758ce3d096be83dc37bd56da48ed99efb8813b", + "sha256:511ec0c00840e12fb4e852e4db58fa6a01ca4da72f36a9766fae344c3d502033", + "sha256:53e7438fef0c97bc248f88ba1edd10268cd94d5609970aaf87abbe493691af87", + "sha256:569f9ee3025682afda6e9b0f5bb14897c0db03f1a1dc088b083dd36e743f92bb", + "sha256:593853aa1ac6dcc6405324d877544c596c9d948ef20d2e9512a0f5d2d3202356", + "sha256:5b0a07158360d22492f9abd02a0f2ee7981b33f0646bf796598b7673f6bbab14", + "sha256:7ca3db38a61f3655a2613ee2c190d63639215a7a736d3c64cc7bbdb002ce6310", + "sha256:7d1cc7acc9ce55179616cf72154f9e648136ea55987edf84addbcd9886ffeba2", + "sha256:88b51153657612aea68fa684a5b88037597925260392b7bb4509d4f9b0bdd889", + "sha256:955ec084f549128fa2702f0b2dc696392001d986b71acd8fd47424f28289a9c3", + "sha256:b251c7092cbb6d789d62dc9c9e7c4fb448c9138b51285c36aeb72462cad3600e", + "sha256:bd82b684bb498c60ef47bb1541a50e6d006dde8579934dcbdbc61d67d1ea70d9", + "sha256:bfe102659e2ec13b86c7f3b1db6c9a4e7beea4255058d006351339e6b342d5d2", + "sha256:c1e4e39e43057396a5e9d069bfbb6ffeee892e40c5d2effbd8cd71f34ee66c4d", + "sha256:cb2b74c123f65e8166f7e1265829a6c8ed755c3cd16d7f50e75a83456a5f3fd7", + "sha256:cca38ded59105f7705ef6ffe1e960b8db6c7d8279c1e71654a4775ab4454ca15", + "sha256:cf908840896f7aa62d0ec693beb53264b154f972eb8226fb864ac38975590c4f", + "sha256:d095a7b473f8a95f7efe821f92058c8a2ecfb18f8db6677ae3819e15dc11aaae", + "sha256:d22b4297e7e4225ccf01f1aa55e7a96412ea0796b532dd614c3fcbafa341128e", + "sha256:d4a2b578a7a70e0c71f662705262f87a456f1e6c1e40ada7ea699abaf070a76d", + "sha256:ddeb42a3d5419434742bf4cc71c9eaa22df3b76808e23a82bd0b0bd360f1a9f1", + "sha256:e65a5aa1670db6263f19fdc03daee1d7dbbadb5cb67fd0a1f16033659db13c1d", + "sha256:eaad65bd20955131bcdb3967a4dea66b4e4d4ca488efed7c00d91ee0173387e8", + "sha256:f45fba420b94165c17896861bb0e8b27fb7abdcedfeb154895d8553df90b7b00" ], - "version": "==5.0" + "version": "==5.0.2" }, "entrypoints": { "hashes": [ @@ -1241,10 +1249,10 @@ }, "packaging": { "hashes": [ - "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", - "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" + "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", + "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" ], - "version": "==19.2" + "version": "==20.0" }, "pluggy": { "hashes": [ @@ -1255,10 +1263,10 @@ }, "py": { "hashes": [ - "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", - "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa", + "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0" ], - "version": "==1.8.0" + "version": "==1.8.1" }, "pycodestyle": { "hashes": [ @@ -1276,10 +1284,10 @@ }, "pyparsing": { "hashes": [ - "sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f", - "sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a" + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" ], - "version": "==2.4.5" + "version": "==2.4.6" }, "pytest": { "hashes": [ @@ -1316,10 +1324,10 @@ }, "wcwidth": { "hashes": [ - "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", - "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", + "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" ], - "version": "==0.1.7" + "version": "==0.1.8" }, "zipp": { "hashes": [ From f5452055f607fcdba8e407a78fa106ff172c1155 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 10:31:52 +0100 Subject: [PATCH 073/220] fix: Fixed vt_graph imports --- misp_modules/lib/__init__.py | 2 ++ misp_modules/lib/vt_graph_parser/__init__.py | 8 ++------ misp_modules/lib/vt_graph_parser/helpers/__init__.py | 3 +++ misp_modules/lib/vt_graph_parser/helpers/parsers.py | 2 +- misp_modules/lib/vt_graph_parser/importers/__init__.py | 7 +------ misp_modules/lib/vt_graph_parser/importers/base.py | 2 +- .../lib/vt_graph_parser/importers/pymisp_response.py | 4 ++-- misp_modules/modules/export_mod/vt_graph.py | 2 +- 8 files changed, 13 insertions(+), 17 deletions(-) diff --git a/misp_modules/lib/__init__.py b/misp_modules/lib/__init__.py index 57a2505..c078cf7 100644 --- a/misp_modules/lib/__init__.py +++ b/misp_modules/lib/__init__.py @@ -1 +1,3 @@ +from .vt_graph_parser import * # noqa + all = ['joe_parser', 'lastline_api'] diff --git a/misp_modules/lib/vt_graph_parser/__init__.py b/misp_modules/lib/vt_graph_parser/__init__.py index 2a4d339..abc02c5 100644 --- a/misp_modules/lib/vt_graph_parser/__init__.py +++ b/misp_modules/lib/vt_graph_parser/__init__.py @@ -4,9 +4,5 @@ This module provides methods to import graph from misp. """ -from lib.vt_graph_parser.importers import from_pymisp_response - - -__all__ = [ - "from_pymisp_response" -] +from .helpers import * # noqa +from .importers import * # noqa diff --git a/misp_modules/lib/vt_graph_parser/helpers/__init__.py b/misp_modules/lib/vt_graph_parser/helpers/__init__.py index 336faee..7e0ec86 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/__init__.py +++ b/misp_modules/lib/vt_graph_parser/helpers/__init__.py @@ -2,3 +2,6 @@ This modules provides functions and attributes to help MISP importers. """ + + +all = ["parsers", "rules", "wrappers"] diff --git a/misp_modules/lib/vt_graph_parser/helpers/parsers.py b/misp_modules/lib/vt_graph_parser/helpers/parsers.py index c621595..8ca5745 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/parsers.py +++ b/misp_modules/lib/vt_graph_parser/helpers/parsers.py @@ -4,7 +4,7 @@ This module provides parsers for MISP inputs. """ -from lib.vt_graph_parser.helpers.wrappers import MispAttribute +from vt_graph_parser.helpers.wrappers import MispAttribute MISP_INPUT_ATTR = [ diff --git a/misp_modules/lib/vt_graph_parser/importers/__init__.py b/misp_modules/lib/vt_graph_parser/importers/__init__.py index 129d870..c59197c 100644 --- a/misp_modules/lib/vt_graph_parser/importers/__init__.py +++ b/misp_modules/lib/vt_graph_parser/importers/__init__.py @@ -4,9 +4,4 @@ This module provides methods to import graphs from MISP. """ -from lib.vt_graph_parser.importers.pymisp_response import from_pymisp_response - - -__all__ = [ - "from_pymisp_response" -] +__all__ = ["base", "pymisp_response"] diff --git a/misp_modules/lib/vt_graph_parser/importers/base.py b/misp_modules/lib/vt_graph_parser/importers/base.py index 4d9b855..ed5c0fc 100644 --- a/misp_modules/lib/vt_graph_parser/importers/base.py +++ b/misp_modules/lib/vt_graph_parser/importers/base.py @@ -5,7 +5,7 @@ This module provides a common method to import graph from misp attributes. import vt_graph_api -from lib.vt_graph_parser.helpers.rules import MispEventInitialRule +from vt_graph_parser.helpers.rules import MispEventInitialRule def import_misp_graph( diff --git a/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py index 86a3b25..e0e834b 100644 --- a/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +++ b/misp_modules/lib/vt_graph_parser/importers/pymisp_response.py @@ -5,8 +5,8 @@ response payload giving by MISP API directly. """ -from lib.vt_graph_parser.helpers.parsers import parse_pymisp_response -from lib.vt_graph_parser.importers.base import import_misp_graph +from vt_graph_parser.helpers.parsers import parse_pymisp_response +from vt_graph_parser.importers.base import import_misp_graph def from_pymisp_response( diff --git a/misp_modules/modules/export_mod/vt_graph.py b/misp_modules/modules/export_mod/vt_graph.py index d8b3359..70c1952 100644 --- a/misp_modules/modules/export_mod/vt_graph.py +++ b/misp_modules/modules/export_mod/vt_graph.py @@ -3,7 +3,7 @@ import base64 import json -from lib.vt_graph_parser import from_pymisp_response +from vt_graph_parser.importers.pymisp_response import from_pymisp_response misperrors = { From 35c438e6ee536dd51e5462e3b82d21a962571989 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 10:38:12 +0100 Subject: [PATCH 074/220] fix: typo --- misp_modules/lib/vt_graph_parser/helpers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/lib/vt_graph_parser/helpers/__init__.py b/misp_modules/lib/vt_graph_parser/helpers/__init__.py index 7e0ec86..8f9f660 100644 --- a/misp_modules/lib/vt_graph_parser/helpers/__init__.py +++ b/misp_modules/lib/vt_graph_parser/helpers/__init__.py @@ -4,4 +4,4 @@ This modules provides functions and attributes to help MISP importers. """ -all = ["parsers", "rules", "wrappers"] +__all__ = ["parsers", "rules", "wrappers"] From b3bc533bc3d9608b1d56d764fb3c7a05c1ffc17b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 15:02:59 +0100 Subject: [PATCH 075/220] chg: Making ipasn module return asn object(s) - Latest changes on the returned value as string broke the freetext parser, because no asn number could be parsed when we return the full json blob as a freetext attribute - Now returning asn object(s) with a reference to the initial attribute --- misp_modules/modules/expansion/ipasn.py | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/misp_modules/modules/expansion/ipasn.py b/misp_modules/modules/expansion/ipasn.py index cfdbaf5..ca10a7a 100755 --- a/misp_modules/modules/expansion/ipasn.py +++ b/misp_modules/modules/expansion/ipasn.py @@ -2,22 +2,40 @@ import json from pyipasnhistory import IPASNHistory +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['ip-src', 'ip-dst'], 'output': ['freetext']} +mispattributes = {'input': ['ip-src', 'ip-dst'], 'format': 'misp_standard'} moduleinfo = {'version': '0.1', 'author': 'Raphaël Vinot', 'description': 'Query an IP ASN history service (https://github.com/CIRCL/IP-ASN-history.git)', 'module-type': ['expansion', 'hover']} +def parse_result(attribute, values): + event = MISPEvent() + initial_attribute = MISPAttribute() + initial_attribute.from_dict(**attribute) + event.add_attribute(**initial_attribute) + mapping = {'asn': ('AS', 'asn'), 'prefix': ('ip-src', 'subnet-announced')} + print(values) + for last_seen, response in values['response'].items(): + asn = MISPObject('asn') + asn.add_attribute('last-seen', **{'type': 'datetime', 'value': last_seen}) + for feature, attribute_fields in mapping.items(): + attribute_type, object_relation = attribute_fields + asn.add_attribute(object_relation, **{'type': attribute_type, 'value': response[feature]}) + asn.add_reference(initial_attribute.uuid, 'related-to') + event.add_object(**asn) + event = json.loads(event.to_json()) + return {key: event[key] for key in ('Attribute', 'Object')} + + def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('ip-src'): - toquery = request['ip-src'] - elif request.get('ip-dst'): - toquery = request['ip-dst'] + if request.get('attribute') and request['attribute'].get('type') in mispattributes['input']: + toquery = request['attribute']['value'] else: misperrors['error'] = "Unsupported attributes type" return misperrors @@ -28,7 +46,7 @@ def handler(q=False): if not values: misperrors['error'] = 'Unable to find the history of this IP' return misperrors - return {'results': [{'types': mispattributes['output'], 'values': [str(values)]}]} + return {'results': parse_result(request['attribute'], values)} def introspection(): From 8db9891c838ba49e7709653a849a5c82344e2ce9 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 15:12:52 +0100 Subject: [PATCH 076/220] fix: Updated ipasn test following the latest changes on the module --- tests/test_expansions.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 93ee69d..c6d5944 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -237,9 +237,7 @@ class TestExpansions(unittest.TestCase): def test_ipasn(self): query = {"module": "ipasn", "ip-dst": "1.1.1.1"} response = self.misp_modules_post(query) - key = list(self.get_values(response)['response'].keys())[0] - entry = self.get_values(response)['response'][key]['asn'] - self.assertEqual(entry, '13335') + self.assertEqual(self.get_object(response), 'asn') def test_macaddess_io(self): module_name = 'macaddress_io' From 31a74a10c1815245f8cc2c245e52840127301e2e Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 15:37:54 +0100 Subject: [PATCH 077/220] fix: Fixed ipasn test input format + module version updated --- misp_modules/modules/expansion/ipasn.py | 2 +- tests/test_expansions.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/ipasn.py b/misp_modules/modules/expansion/ipasn.py index ca10a7a..3c6867c 100755 --- a/misp_modules/modules/expansion/ipasn.py +++ b/misp_modules/modules/expansion/ipasn.py @@ -6,7 +6,7 @@ from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['ip-src', 'ip-dst'], 'format': 'misp_standard'} -moduleinfo = {'version': '0.1', 'author': 'Raphaël Vinot', +moduleinfo = {'version': '0.2', 'author': 'Raphaël Vinot', 'description': 'Query an IP ASN history service (https://github.com/CIRCL/IP-ASN-history.git)', 'module-type': ['expansion', 'hover']} diff --git a/tests/test_expansions.py b/tests/test_expansions.py index c6d5944..879c590 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -235,7 +235,10 @@ class TestExpansions(unittest.TestCase): self.assertTrue(value.startswith('{"ip":"1.1.1.1","status":"ok"')) def test_ipasn(self): - query = {"module": "ipasn", "ip-dst": "1.1.1.1"} + query = {"module": "ipasn", + "attribute": {"type": "ip-src", + "value": "149.13.33.14", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}} response = self.misp_modules_post(query) self.assertEqual(self.get_object(response), 'asn') From a88f19942f75885b74678314c710925d1eafa29a Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 10 Jan 2020 16:19:00 +0100 Subject: [PATCH 078/220] new: Updated ipasn and added vt_graph documentation --- README.md | 37 ++++++++++++++++++------------------ doc/README.md | 24 +++++++++++++++++++++-- doc/expansion/ipasn.json | 4 ++-- doc/export_mod/vt_graph.json | 9 +++++++++ 4 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 doc/export_mod/vt_graph.json diff --git a/README.md b/README.md index d0296a8..af78ca5 100644 --- a/README.md +++ b/README.md @@ -89,27 +89,28 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj ### Export modules -* [CEF](misp_modules/modules/export_mod/cef_export.py) module to export Common Event Format (CEF). -* [Cisco FireSight Manager ACL rule](misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py) module to export as rule for the Cisco FireSight manager ACL. -* [GoAML export](misp_modules/modules/export_mod/goamlexport.py) module to export in [GoAML format](http://goaml.unodc.org/goaml/en/index.html). -* [Lite Export](misp_modules/modules/export_mod/liteexport.py) module to export a lite event. -* [PDF export](misp_modules/modules/export_mod/pdfexport.py) module to export an event in PDF. -* [Mass EQL Export](misp_modules/modules/export_mod/mass_eql_export.py) module to export applicable attributes from an event to a mass EQL query. -* [Nexthink query format](misp_modules/modules/export_mod/nexthinkexport.py) module to export in Nexthink query format. -* [osquery](misp_modules/modules/export_mod/osqueryexport.py) module to export in [osquery](https://osquery.io/) query format. -* [ThreatConnect](misp_modules/modules/export_mod/threat_connect_export.py) module to export in ThreatConnect CSV format. -* [ThreatStream](misp_modules/modules/export_mod/threatStream_misp_export.py) module to export in ThreatStream format. +* [CEF](misp_modules/modules/export_mod/cef_export.py) - module to export Common Event Format (CEF). +* [Cisco FireSight Manager ACL rule](misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py) - module to export as rule for the Cisco FireSight manager ACL. +* [GoAML export](misp_modules/modules/export_mod/goamlexport.py) - module to export in [GoAML format](http://goaml.unodc.org/goaml/en/index.html). +* [Lite Export](misp_modules/modules/export_mod/liteexport.py) - module to export a lite event. +* [PDF export](misp_modules/modules/export_mod/pdfexport.py) - module to export an event in PDF. +* [Mass EQL Export](misp_modules/modules/export_mod/mass_eql_export.py) - module to export applicable attributes from an event to a mass EQL query. +* [Nexthink query format](misp_modules/modules/export_mod/nexthinkexport.py) - module to export in Nexthink query format. +* [osquery](misp_modules/modules/export_mod/osqueryexport.py) - module to export in [osquery](https://osquery.io/) query format. +* [ThreatConnect](misp_modules/modules/export_mod/threat_connect_export.py) - module to export in ThreatConnect CSV format. +* [ThreatStream](misp_modules/modules/export_mod/threatStream_misp_export.py) - module to export in ThreatStream format. +* [VirusTotal Graph](misp_modules/modules/export_mod/vt_graph.py) - Module to create a VirusTotal graph out of an event. ### Import modules -* [CSV import](misp_modules/modules/import_mod/csvimport.py) Customizable CSV import module. -* [Cuckoo JSON](misp_modules/modules/import_mod/cuckooimport.py) Cuckoo JSON import. -* [Email Import](misp_modules/modules/import_mod/email_import.py) Email import module for MISP to import basic metadata. -* [GoAML import](misp_modules/modules/import_mod/goamlimport.py) Module to import [GoAML](http://goaml.unodc.org/goaml/en/index.html) XML format. -* [Joe Sandbox import](misp_modules/modules/import_mod/joe_import.py) Parse data from a Joe Sandbox json report. -* [Lastline import](misp_modules/modules/import_mod/lastline_import.py) Module to import Lastline analysis reports. -* [OCR](misp_modules/modules/import_mod/ocr.py) Optical Character Recognition (OCR) module for MISP to import attributes from images, scan or faxes. -* [OpenIOC](misp_modules/modules/import_mod/openiocimport.py) OpenIOC import based on PyMISP library. +* [CSV import](misp_modules/modules/import_mod/csvimport.py) - Customizable CSV import module. +* [Cuckoo JSON](misp_modules/modules/import_mod/cuckooimport.py) - Cuckoo JSON import. +* [Email Import](misp_modules/modules/import_mod/email_import.py) - Email import module for MISP to import basic metadata. +* [GoAML import](misp_modules/modules/import_mod/goamlimport.py) - Module to import [GoAML](http://goaml.unodc.org/goaml/en/index.html) XML format. +* [Joe Sandbox import](misp_modules/modules/import_mod/joe_import.py) - Parse data from a Joe Sandbox json report. +* [Lastline import](misp_modules/modules/import_mod/lastline_import.py) - Module to import Lastline analysis reports. +* [OCR](misp_modules/modules/import_mod/ocr.py) - Optical Character Recognition (OCR) module for MISP to import attributes from images, scan or faxes. +* [OpenIOC](misp_modules/modules/import_mod/openiocimport.py) - OpenIOC import based on PyMISP library. * [ThreatAnalyzer](misp_modules/modules/import_mod/threatanalyzer_import.py) - An import module to process ThreatAnalyzer archive.zip/analysis.json sandbox exports. * [VMRay](misp_modules/modules/import_mod/vmray_import.py) - An import module to process VMRay export. diff --git a/doc/README.md b/doc/README.md index 64df950..2049803 100644 --- a/doc/README.md +++ b/doc/README.md @@ -532,11 +532,11 @@ Module to access intelmqs eventdb. Module to query an IP ASN history service (https://github.com/D4-project/IPASN-History). - **features**: ->This module takes an IP address attribute as input and queries the CIRCL IPASN service to get additional information about the input. +>This module takes an IP address attribute as input and queries the CIRCL IPASN service. The result of the query is the latest asn related to the IP address, that is returned as a MISP object. - **input**: >An IP address MISP attribute. - **output**: ->Text describing additional information about the input after a query on the IPASN-history database. +>Asn object(s) objects related to the IP address used as input. - **references**: >https://github.com/D4-project/IPASN-History - **requirements**: @@ -1586,6 +1586,26 @@ Module to export a structured CSV file for uploading to ThreatConnect. ----- +#### [vt_graph](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/vt_graph.py) + + + +This module is used to create a VirusTotal Graph from a MISP event. +- **features**: +>The module takes the MISP event as input and queries the VirusTotal Graph API to create a new graph out of the event. +> +>Once the graph is ready, we get the url of it, which is returned so we can view it on VirusTotal. +- **input**: +>A MISP event. +- **output**: +>Link of the VirusTotal Graph created for the event. +- **references**: +>https://www.virustotal.com/gui/graph-overview +- **requirements**: +>vt_graph_api, the python library to query the VirusTotal graph API + +----- + ## Import Modules #### [csvimport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/csvimport.py) diff --git a/doc/expansion/ipasn.json b/doc/expansion/ipasn.json index 68b10d1..8caed92 100644 --- a/doc/expansion/ipasn.json +++ b/doc/expansion/ipasn.json @@ -2,7 +2,7 @@ "description": "Module to query an IP ASN history service (https://github.com/D4-project/IPASN-History).", "requirements": ["pyipasnhistory: Python library to access IPASN-history instance"], "input": "An IP address MISP attribute.", - "output": "Text describing additional information about the input after a query on the IPASN-history database.", + "output": "Asn object(s) objects related to the IP address used as input.", "references": ["https://github.com/D4-project/IPASN-History"], - "features": "This module takes an IP address attribute as input and queries the CIRCL IPASN service to get additional information about the input." + "features": "This module takes an IP address attribute as input and queries the CIRCL IPASN service. The result of the query is the latest asn related to the IP address, that is returned as a MISP object." } diff --git a/doc/export_mod/vt_graph.json b/doc/export_mod/vt_graph.json new file mode 100644 index 0000000..e317730 --- /dev/null +++ b/doc/export_mod/vt_graph.json @@ -0,0 +1,9 @@ +{ + "description": "This module is used to create a VirusTotal Graph from a MISP event.", + "logo": "logos/virustotal.png", + "requirements": ["vt_graph_api, the python library to query the VirusTotal graph API"], + "features": "The module takes the MISP event as input and queries the VirusTotal Graph API to create a new graph out of the event.\n\nOnce the graph is ready, we get the url of it, which is returned so we can view it on VirusTotal.", + "references": ["https://www.virustotal.com/gui/graph-overview"], + "input": "A MISP event.", + "output": "Link of the VirusTotal Graph created for the event." +} From 610c99ce7b9bb6b034d2f3e50e247229586be263 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Fri, 17 Jan 2020 10:58:31 +0100 Subject: [PATCH 079/220] Fix error message in Public VT module --- misp_modules/modules/expansion/virustotal_public.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/virustotal_public.py b/misp_modules/modules/expansion/virustotal_public.py index 69c2c85..a6c093a 100644 --- a/misp_modules/modules/expansion/virustotal_public.py +++ b/misp_modules/modules/expansion/virustotal_public.py @@ -85,8 +85,9 @@ class DomainQuery(VirusTotalParser): whois_object = MISPObject(whois) whois_object.add_attribute('text', type='text', value=query_result[whois]) self.misp_event.add_object(**whois_object) - siblings = (self.parse_siblings(domain) for domain in query_result['domain_siblings']) - self.parse_resolutions(query_result['resolutions'], query_result['subdomains'], siblings) + if 'domain_siblings' in query_result['domain_siblings']: + siblings = (self.parse_siblings(domain) for domain in query_result['domain_siblings']) + self.parse_resolutions(query_result['resolutions'], query_result['subdomains'], siblings) self.parse_urls(query_result) def parse_siblings(self, domain): From 036933ea14d4f90bd7c05e2466837d0c8c107ab7 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Fri, 17 Jan 2020 11:26:35 +0100 Subject: [PATCH 080/220] 2nd fix for VT Public module --- misp_modules/modules/expansion/virustotal_public.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/misp_modules/modules/expansion/virustotal_public.py b/misp_modules/modules/expansion/virustotal_public.py index a6c093a..f31855e 100644 --- a/misp_modules/modules/expansion/virustotal_public.py +++ b/misp_modules/modules/expansion/virustotal_public.py @@ -85,9 +85,10 @@ class DomainQuery(VirusTotalParser): whois_object = MISPObject(whois) whois_object.add_attribute('text', type='text', value=query_result[whois]) self.misp_event.add_object(**whois_object) - if 'domain_siblings' in query_result['domain_siblings']: - siblings = (self.parse_siblings(domain) for domain in query_result['domain_siblings']) - self.parse_resolutions(query_result['resolutions'], query_result['subdomains'], siblings) + if 'domain_siblings' in query_result: + siblings = (self.parse_siblings(domain) for domain in query_result['domain_siblings']) + if 'subdomains' in query_result: + self.parse_resolutions(query_result['resolutions'], query_result['subdomains'], siblings) self.parse_urls(query_result) def parse_siblings(self, domain): From 66bf650b79e8a1c5d094a0af79fae5f2ac9aae40 Mon Sep 17 00:00:00 2001 From: Stefano Ortolani Date: Sat, 28 Dec 2019 15:57:15 +0100 Subject: [PATCH 081/220] change: migrate to analysis API when submitting tasks to Lastline --- doc/README.md | 4 +- doc/expansion/lastline_query.json | 2 +- doc/expansion/lastline_submit.json | 2 +- doc/import_mod/lastline_import.json | 2 +- misp_modules/lib/lastline_api.py | 1046 ++++++++++------- .../modules/expansion/lastline_query.py | 13 +- .../modules/expansion/lastline_submit.py | 30 +- .../modules/import_mod/lastline_import.py | 13 +- 8 files changed, 638 insertions(+), 474 deletions(-) diff --git a/doc/README.md b/doc/README.md index 2049803..7e6bee3 100644 --- a/doc/README.md +++ b/doc/README.md @@ -613,6 +613,7 @@ A module to submit files or URLs to Joe Sandbox for an advanced analysis, and re Query Lastline with an analysis link and parse the report into MISP attributes and objects. The analysis link can also be retrieved from the output of the [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) expansion module. - **features**: +>The module requires a Lastline Portal `username` and `password`. >The module uses the new format and it is able to return MISP attributes and objects. >The module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module. - **input**: @@ -630,7 +631,7 @@ The analysis link can also be retrieved from the output of the [lastline_submit] Module to submit a file or URL to Lastline. - **features**: ->The module requires a Lastline API key and token (or username and password). +>The module requires a Lastline Analysis `api_token` and `key`. >When the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module. - **input**: >File or URL to submit to Lastline. @@ -1701,6 +1702,7 @@ A module to import data from a Joe Sandbox analysis json report. Module to import and parse reports from Lastline analysis links. - **features**: +>The module requires a Lastline Portal `username` and `password`. >The module uses the new format and it is able to return MISP attributes and objects. >The module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module. - **input**: diff --git a/doc/expansion/lastline_query.json b/doc/expansion/lastline_query.json index 0d5da39..6165890 100644 --- a/doc/expansion/lastline_query.json +++ b/doc/expansion/lastline_query.json @@ -5,5 +5,5 @@ "input": "Link to a Lastline analysis.", "output": "MISP attributes and objects parsed from the analysis report.", "references": ["https://www.lastline.com"], - "features": "The module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module." + "features": "The module requires a Lastline Portal `username` and `password`.\nThe module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module." } diff --git a/doc/expansion/lastline_submit.json b/doc/expansion/lastline_submit.json index cf5ef57..d053f55 100644 --- a/doc/expansion/lastline_submit.json +++ b/doc/expansion/lastline_submit.json @@ -5,5 +5,5 @@ "input": "File or URL to submit to Lastline.", "output": "Link to the report generated by Lastline.", "references": ["https://www.lastline.com"], - "features": "The module requires a Lastline API key and token (or username and password).\nWhen the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module." + "features": "The module requires a Lastline Analysis `api_token` and `key`.\nWhen the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module." } diff --git a/doc/import_mod/lastline_import.json b/doc/import_mod/lastline_import.json index 1d4c15d..99414e0 100644 --- a/doc/import_mod/lastline_import.json +++ b/doc/import_mod/lastline_import.json @@ -5,5 +5,5 @@ "input": "Link to a Lastline analysis.", "output": "MISP attributes and objects parsed from the analysis report.", "references": ["https://www.lastline.com"], - "features": "The module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module." + "features": "The module requires a Lastline Portal `username` and `password`.\nThe module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module." } diff --git a/misp_modules/lib/lastline_api.py b/misp_modules/lib/lastline_api.py index a6912b8..83726ad 100644 --- a/misp_modules/lib/lastline_api.py +++ b/misp_modules/lib/lastline_api.py @@ -30,19 +30,21 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -import ipaddress +import abc import logging +import io +import ipaddress +import pymisp import re import requests -import pymisp - from urllib import parse -DEFAULT_LASTLINE_API = "https://user.lastline.com/papi" +DEFAULT_LL_PORTAL_API_URL = "https://user.lastline.com/papi" +DEFAULT_LL_ANALYSIS_API_URL = "https://analysis.lastline.com" -HOSTED_LASTLINE_DOMAINS = frozenset([ +LL_HOSTED_DOMAINS = frozenset([ "user.lastline.com", "user.emea.lastline.com", ]) @@ -53,61 +55,66 @@ def purge_none(d): return {k: v for k, v in d.items() if v is not None} -def get_analysis_link(api_url, uuid): +def get_task_link(uuid, analysis_url=None, portal_url=None): """ - Get the analysis link of a task given the task uuid. + Get the task link given the task uuid and at least one API url. - :param str api_url: the URL :param str uuid: the task uuid + :param str|None analysis_url: the URL to the analysis API endpoint + :param str|None portal_url: the URL to the portal API endpoint :rtype: str - :return: the analysis link + :return: the task link + :raises ValueError: if not enough parameters have been provided """ + if not analysis_url and not portal_url: + raise ValueError("Neither analysis URL or portal URL have been specified") + if analysis_url: + portal_url = "{}/papi".format(analysis_url.replace("analysis.", "user.")) portal_url_path = "../portal#/analyst/task/{}/overview".format(uuid) - analysis_link = parse.urljoin(api_url, portal_url_path) - return analysis_link + return parse.urljoin(portal_url, portal_url_path) -def get_uuid_from_link(analysis_link): +def get_portal_url_from_task_link(task_link): """ - Return task uuid from link or raise ValueError exception. + Return the portal API url related to the provided task link. - :param str analysis_link: a link + :param str task_link: a link + :rtype: str + :return: the portal API url + """ + parsed_uri = parse.urlparse(task_link) + return "{uri.scheme}://{uri.netloc}/papi".format(uri=parsed_uri) + + +def get_uuid_from_task_link(task_link): + """ + Return the uuid from a task link. + + :param str task_link: a link :rtype: str :return: the uuid :raises ValueError: if the link contains not task uuid """ try: - return re.findall("[a-fA-F0-9]{32}", analysis_link)[0] + return re.findall("[a-fA-F0-9]{32}", task_link)[0] except IndexError: raise ValueError("Link does not contain a valid task uuid") -def is_analysis_hosted(analysis_link): +def is_task_hosted(task_link): """ - Return whether the analysis link is pointing to a hosted submission. + Return whether the portal link is pointing to a hosted submission. - :param str analysis_link: a link + :param str task_link: a link :rtype: boolean - :return: whether the link is hosted + :return: whether the link points to a hosted analysis """ - for domain in HOSTED_LASTLINE_DOMAINS: - if domain in analysis_link: + for domain in LL_HOSTED_DOMAINS: + if domain in task_link: return True return False -def get_api_url_from_link(analysis_link): - """ - Return the API url related to the provided analysis link. - - :param str analysis_link: a link - :rtype: str - :return: the API url - """ - parsed_uri = parse.urlparse(analysis_link) - return "{uri.scheme}://{uri.netloc}/papi".format(uri=parsed_uri) - - class InvalidArgument(Exception): """Error raised invalid.""" @@ -135,6 +142,572 @@ class ApiError(Error): return "{}{}".format(self.error_msg, error_code) +class LastlineAbstractClient(abc.ABC): + """"A very basic HTTP client providing basic functionality.""" + + __metaclass__ = abc.ABCMeta + + SUB_APIS = ('analysis', 'authentication', 'knowledgebase', 'login') + FORMATS = ["json", "xml"] + + @classmethod + def sanitize_login_params(cls, api_key, api_token, username, password): + """ + Return a dictionary with either API or USER credentials. + + :param str|None api_key: the API key + :param str|None api_token: the API token + :param str|None username: the username + :param str|None password: the password + :rtype: dict[str, str] + :return: the dictionary + :raises InvalidArgument: if too many values are invalid + """ + if api_key and api_token: + return { + "key": api_key, + "api_token": api_token, + } + elif username and password: + return { + "username": username, + "password": password, + } + else: + raise InvalidArgument("Arguments provided do not contain valid data") + + @classmethod + def get_login_params_from_dict(cls, d): + """ + Get the module configuration from a ConfigParser object. + + :param dict[str, str] d: the dictionary + :rtype: dict[str, str] + :return: the parsed configuration + """ + api_key = d.get("key") + api_token = d.get("api_token") + username = d.get("username") + password = d.get("password") + return cls.sanitize_login_params(api_key, api_token, username, password) + + @classmethod + def get_login_params_from_conf(cls, conf, section_name): + """ + Get the module configuration from a ConfigParser object. + + :param ConfigParser conf: the conf object + :param str section_name: the section name + :rtype: dict[str, str] + :return: the parsed configuration + """ + api_key = conf.get(section_name, "key", fallback=None) + api_token = conf.get(section_name, "api_token", fallback=None) + username = conf.get(section_name, "username", fallback=None) + password = conf.get(section_name, "password", fallback=None) + return cls.sanitize_login_params(api_key, api_token, username, password) + + @classmethod + def load_from_conf(cls, conf, section_name): + """ + Load client from a ConfigParser object. + + :param ConfigParser conf: the conf object + :param str section_name: the section name + :rtype: T <- LastlineAbstractClient + :return: the loaded client + """ + url = conf.get(section_name, "url") + return cls(url, cls.get_login_params_from_conf(conf, section_name)) + + def __init__(self, api_url, login_params, timeout=60, verify_ssl=True): + """ + Instantiate a Lastline mini client. + + :param str api_url: the URL of the API + :param dict[str, str]: the login parameters + :param int timeout: the timeout + :param boolean verify_ssl: whether to verify the SSL certificate + """ + self._url = api_url + self._login_params = login_params + self._timeout = timeout + self._verify_ssl = verify_ssl + self._session = None + self._logger = logging.getLogger(__name__) + + @abc.abstractmethod + def _login(self): + """Login using account-based or key-based methods.""" + + def _is_logged_in(self): + """Return whether we have an active session.""" + return self._session is not None + + @staticmethod + def _parse_response(response): + """ + Parse the response. + + :param requests.Response response: the response + :rtype: tuple(str|None, Error|ApiError) + :return: a tuple with mutually exclusive fields (either the response or the error) + """ + try: + ret = response.json() + if "success" not in ret: + return None, Error("no success field in response") + + if not ret["success"]: + error_msg = ret.get("error", "") + error_code = ret.get("error_code", None) + return None, ApiError(error_msg, error_code) + + if "data" not in ret: + return None, Error("no data field in response") + + return ret["data"], None + except ValueError as e: + return None, Error("Response not json {}".format(e)) + + def _handle_response(self, response, raw=False): + """ + Check a response for issues and parse the return. + + :param requests.Response response: the response + :param boolean raw: whether the raw body should be returned + :rtype: str + :return: if raw, return the response content; if not raw, the data field + :raises: CommunicationError, ApiError, Error + """ + # Check for HTTP errors, and re-raise in case + try: + response.raise_for_status() + except requests.RequestException as e: + _, err = self._parse_response(response) + if isinstance(err, ApiError): + err_msg = "{}: {}".format(e, err.error_msg) + else: + err_msg = "{}".format(e) + raise CommunicationError(err_msg) + + # Otherwise return the data (either parsed or not) but reraise if we have an API error + if raw: + return response.content + data, err = self._parse_response(response) + if err: + raise err + return data + + def _build_url(self, sub_api, parts, requested_format="json"): + if sub_api not in self.SUB_APIS: + raise InvalidArgument(sub_api) + if requested_format not in self.FORMATS: + raise InvalidArgument(requested_format) + num_parts = 2 + len(parts) + pattern = "/".join(["%s"] * num_parts) + ".%s" + params = [self._url, sub_api] + parts + [requested_format] + return pattern % tuple(params) + + def post(self, module, function, params=None, data=None, files=None, fmt="json"): + if isinstance(function, list): + functions = function + else: + functions = [function] if function else [] + url = self._build_url(module, functions, requested_format=fmt) + return self.do_request( + url=url, + method="POST", + params=params, + data=data, + files=files, + fmt=fmt, + ) + + def get(self, module, function, params=None, fmt="json"): + if isinstance(function, list): + functions = function + else: + functions = [function] if function else [] + url = self._build_url(module, functions, requested_format=fmt) + return self.do_request( + url=url, + method="GET", + params=params, + fmt=fmt, + ) + + def do_request( + self, + method, + url, + params=None, + data=None, + files=None, + fmt="json", + raw=False, + raw_response=False, + headers=None, + stream_response=False + ): + if raw_response: + raw = True + + if fmt: + fmt = fmt.lower().strip() + if fmt not in self.FORMATS: + raise InvalidArgument("Only json, xml, html and pdf supported") + elif not raw: + raise InvalidArgument("Unformatted response requires raw=True") + + if fmt != "json" and not raw: + raise InvalidArgument("Non-json format requires raw=True") + + if method not in ["POST", "GET"]: + raise InvalidArgument("Only POST and GET supported") + + if not self._is_logged_in(): + self._login() + + try: + try: + response = self._session.request( + method=method, + url=url, + data=data, + params=params, + files=files, + verify=self._verify_ssl, + timeout=self._timeout, + stream=stream_response, + headers=headers, + ) + except requests.RequestException as e: + raise CommunicationError(e) + + if raw_response: + return response + return self._handle_response(response, raw) + + except Error as e: + raise e + + except CommunicationError as e: + raise e + + +class AnalysisClient(LastlineAbstractClient): + + def _login(self): + """ + Creates auth session for malscape-service. + + Credentials are 'key' and 'api_token'. + """ + if self._session is None: + self._session = requests.session() + url = self._build_url("authentication", ["login"]) + self.do_request("POST", url, params=purge_none(self._login_params)) + + def get_progress(self, uuid): + """ + Get the completion progress of a given task. + :param str uuid: the unique identifier of the submitted task + :rtype: dict[str, int] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + url = self._build_url('analysis', ['get_progress']) + params = {'uuid': uuid} + return self.do_request("POST", url, params=params) + + def get_result(self, uuid): + """ + Get report results for a given task. + + :param str uuid: the unique identifier of the submitted task + :rtype: dict[str, any] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + # better: use 'get_results()' but that would break + # backwards-compatibility + url = self._build_url('analysis', ['get']) + params = {'uuid': uuid} + return self.do_request("GET", url, params=params) + + def submit_file( + self, + file_data, + file_name=None, + password=None, + analysis_env=None, + allow_network_traffic=True, + analysis_timeout=None, + bypass_cache=False, + ): + """ + Upload a file to be analyzed. + + :param bytes file_data: the data as a byte sequence + :param str|None file_name: if set, represents the name of the file to submit + :param str|None password: if set, use it to extract the sample + :param str|None analysis_env: if set, e.g windowsxp + :param boolean allow_network_traffic: if set to False, deny network connections + :param int|None analysis_timeout: if set limit the duration of the analysis + :param boolean bypass_cache: whether to re-process a file (requires special permissions) + :rtype: dict[str, any] + :return: a dictionary in the following form if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + file_stream = io.BytesIO(file_data) + api_url = self._build_url("analysis", ["submit", "file"]) + params = purge_none({ + "bypass_cache": bypass_cache and 1 or None, + "analysis_timeout": analysis_timeout, + "analysis_env": analysis_env, + "allow_network_traffic": allow_network_traffic and 1 or None, + "filename": file_name, + "password": password, + "full_report_score": -1, + }) + + files = purge_none({ + # If an explicit filename was provided, we can pass it down to + # python-requests to use it in the multipart/form-data. This avoids + # having python-requests trying to guess the filename based on stream + # attributes. + # + # The problem with this is that, if the filename is not ASCII, then + # this triggers a bug in flask/werkzeug which means the file is + # thrown away. Thus, we just force an ASCII name + "file": ('dummy-ascii-name-for-file-param', file_stream), + }) + + return self.do_request("POST", api_url, params=params, files=files) + + def submit_url( + self, + url, + referer=None, + user_agent=None, + bypass_cache=False, + ): + """ + Upload an URL to be analyzed. + + :param str url: the url to analyze + :param str|None referer: the referer + :param str|None user_agent: the user agent + :param boolean bypass_cache: bypass_cache + :rtype: dict[str, any] + :return: a dictionary like the following if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + api_url = self._build_url("analysis", ["submit", "url"]) + params = purge_none({ + "url": url, + "referer": referer, + "bypass_cache": bypass_cache and 1 or None, + "user_agent": user_agent or None, + }) + return self.do_request("POST", api_url, params=params) + + +class PortalClient(LastlineAbstractClient): + + def _login(self): + """ + Login using account-based or key-based methods. + + Credentials are 'username' and 'password' + """ + if self._session is None: + self._session = requests.session() + self.post("login", function=None, data=self._login_params) + + def get_progress(self, uuid, analysis_instance=None): + """ + Get the completion progress of a given task. + + :param str uuid: the unique identifier of the submitted task + :param str analysis_instance: if set, defines the analysis instance to query + :rtype: dict[str, int] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + params = purge_none({"uuid": uuid, "analysis_instance": analysis_instance}) + return self.get("analysis", "get_progress", params=params) + + def get_result(self, uuid, analysis_instance=None): + """ + Get report results for a given task. + + :param str uuid: the unique identifier of the submitted task + :param str analysis_instance: if set, defines the analysis instance to query + :rtype: dict[str, any] + :return: a dictionary like the the following: + { + "completed": 1, + "progress": 100 + } + """ + params = purge_none( + { + "uuid": uuid, + "analysis_instance": analysis_instance, + "report_format": "json", + } + ) + return self.get("analysis", "get_result", params=params) + + def submit_url( + self, + url, + referer=None, + user_agent=None, + bypass_cache=False, + ): + """ + Upload an URL to be analyzed. + + :param str url: the url to analyze + :param str|None referer: the referer + :param str|None user_agent: the user agent + :param boolean bypass_cache: bypass_cache + :rtype: dict[str, any] + :return: a dictionary like the following if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + params = purge_none( + { + "url": url, + "bypass_cache": bypass_cache, + "referer": referer, + "user_agent": user_agent + } + ) + return self.post("analysis", "submit_url", params=params) + + def submit_file( + self, + file_data, + file_name=None, + password=None, + analysis_env=None, + allow_network_traffic=True, + analysis_timeout=None, + bypass_cache=False, + ): + """ + Upload a file to be analyzed. + + :param bytes file_data: the data as a byte sequence + :param str|None file_name: if set, represents the name of the file to submit + :param str|None password: if set, use it to extract the sample + :param str|None analysis_env: if set, e.g windowsxp + :param boolean allow_network_traffic: if set to False, deny network connections + :param int|None analysis_timeout: if set limit the duration of the analysis + :param boolean bypass_cache: whether to re-process a file (requires special permissions) + :rtype: dict[str, any] + :return: a dictionary in the following form if the analysis is already available: + { + "submission": "2019-11-17 09:33:23", + "child_tasks": [...], + "reports": [...], + "submission_timestamp": "2019-11-18 16:11:04", + "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", + "score": 0, + "analysis_subject": { + "url": "https://www.google.com" + }, + "last_submission_timestamp": "2019-11-18 16:11:04" + } + + OR the following if the analysis is still pending: + + { + "submission_timestamp": "2019-11-18 13:59:25", + "task_uuid": "f3c0ae115d51001017ff8da768fa6049", + } + """ + params = purge_none( + { + "filename": file_name, + "password": password, + "analysis_env": analysis_env, + "allow_network_traffic": allow_network_traffic, + "analysis_timeout": analysis_timeout, + "bypass_cache": bypass_cache, + } + ) + files = {"file": (file_name, file_data, "application/octet-stream")} + return self.post("analysis", "submit_file", params=params, files=files) + + class LastlineResultBaseParser(object): """ This is a parser to extract *basic* information from a Lastline result dictionary. @@ -247,7 +820,7 @@ class LastlineResultBaseParser(object): # Add sandbox info like score and sandbox type o = pymisp.MISPObject(name="sandbox-report") - sandbox_type = "saas" if is_analysis_hosted(analysis_link) else "on-premise" + sandbox_type = "saas" if is_task_hosted(analysis_link) else "on-premise" o.add_attribute("score", result["score"]) o.add_attribute("sandbox-type", sandbox_type) o.add_attribute("{}-sandbox".format(sandbox_type), "lastline") @@ -266,408 +839,3 @@ class LastlineResultBaseParser(object): # Add mitre techniques for technique in self._get_mitre_techniques(result): self.misp_event.add_tag(technique) - - -class LastlineCommunityHTTPClient(object): - """"A very basic HTTP client providing basic functionality.""" - - @classmethod - def sanitize_login_params(cls, api_key, api_token, username, password): - """ - Return a dictionary with either API or USER credentials. - - :param str|None api_key: the API key - :param str|None api_token: the API token - :param str|None username: the username - :param str|None password: the password - :rtype: dict[str, str] - :return: the dictionary - :raises InvalidArgument: if too many values are invalid - """ - if api_key and api_token: - return { - "api_key": api_key, - "api_token": api_token, - } - elif username and password: - return { - "username": username, - "password": password, - } - else: - raise InvalidArgument("Arguments provided do not contain valid data") - - @classmethod - def get_login_params_from_conf(cls, conf, section_name): - """ - Get the module configuration from a ConfigParser object. - - :param ConfigParser conf: the conf object - :param str section_name: the section name - :rtype: dict[str, str] - :return: the parsed configuration - """ - api_key = conf.get(section_name, "api_key", fallback=None) - api_token = conf.get(section_name, "api_token", fallback=None) - username = conf.get(section_name, "username", fallback=None) - password = conf.get(section_name, "password", fallback=None) - return cls.sanitize_login_params(api_key, api_token, username, password) - - @classmethod - def get_login_params_from_request(cls, request): - """ - Get the module configuration from a ConfigParser object. - - :param dict[str, any] request: the request object - :rtype: dict[str, str] - :return: the parsed configuration - """ - api_key = request.get("config", {}).get("api_key") - api_token = request.get("config", {}).get("api_token") - username = request.get("config", {}).get("username") - password = request.get("config", {}).get("password") - return cls.sanitize_login_params(api_key, api_token, username, password) - - def __init__(self, api_url, login_params, timeout=60, verify_ssl=True): - """ - Instantiate a Lastline mini client. - - :param str api_url: the URL of the API - :param dict[str, str]: the login parameters - :param int timeout: the timeout - :param boolean verify_ssl: whether to verify the SSL certificate - """ - self.__url = api_url - self.__login_params = login_params - self.__timeout = timeout - self.__verify_ssl = verify_ssl - self.__session = None - self.__logger = logging.getLogger(__name__) - - def __login(self): - """Login using account-based or key-based methods.""" - if self.__session is None: - self.__session = requests.session() - - login_url = "/".join([self.__url, "login"]) - try: - response = self.__session.request( - method="POST", - url=login_url, - data=self.__login_params, - verify=self.__verify_ssl, - timeout=self.__timeout, - proxies=None, - ) - except requests.RequestException as e: - raise CommunicationError(e) - - self.__handle_response(response) - - def __is_logged_in(self): - """Return whether we have an active session.""" - return self.__session is not None - - @staticmethod - def __parse_response(response): - """ - Parse the response. - - :param requests.Response response: the response - :rtype: tuple(str|None, Error|ApiError) - :return: a tuple with mutually exclusive fields (either the response or the error) - """ - try: - ret = response.json() - if "success" not in ret: - return None, Error("no success field in response") - - if not ret["success"]: - error_msg = ret.get("error", "") - error_code = ret.get("error_code", None) - return None, ApiError(error_msg, error_code) - - if "data" not in ret: - return None, Error("no data field in response") - - return ret["data"], None - except ValueError as e: - return None, Error("Response not json {}".format(e)) - - def __handle_response(self, response, raw=False): - """ - Check a response for issues and parse the return. - - :param requests.Response response: the response - :param boolean raw: whether the raw body should be returned - :rtype: str - :return: if raw, return the response content; if not raw, the data field - :raises: CommunicationError, ApiError, Error - """ - # Check for HTTP errors, and re-raise in case - try: - response.raise_for_status() - except requests.RequestException as e: - _, err = self.__parse_response(response) - if isinstance(err, ApiError): - err_msg = "{}: {}".format(e, err.error_msg) - else: - err_msg = "{}".format(e) - raise CommunicationError(err_msg) - - # Otherwise return the data (either parsed or not) but reraise if we have an API error - if raw: - return response.content - data, err = self.__parse_response(response) - if err: - raise err - return data - - def do_request( - self, - method, - module, - function, - params=None, - data=None, - files=None, - url=None, - fmt="JSON", - raw=False, - raw_response=False, - headers=None, - stream_response=False - ): - if raw_response: - raw = True - - if fmt: - fmt = fmt.lower().strip() - if fmt not in ["json", "xml", "html", "pdf"]: - raise InvalidArgument("Only json, xml, html and pdf supported") - elif not raw: - raise InvalidArgument("Unformatted response requires raw=True") - - if fmt != "json" and not raw: - raise InvalidArgument("Non-json format requires raw=True") - - if method not in ["POST", "GET"]: - raise InvalidArgument("Only POST and GET supported") - - function = function.strip(" /") - if not function: - raise InvalidArgument("No function provided") - - # Login after we verified that all arguments are fine - if not self.__is_logged_in(): - self.__login() - - url_parts = [url or self.__url] - module = module.strip(" /") - if module: - url_parts.append(module) - if fmt: - function_part = "%s.%s" % (function, fmt) - else: - function_part = function - url_parts.append(function_part) - url = "/".join(url_parts) - - try: - try: - response = self.__session.request( - method=method, - url=url, - data=data, - params=params, - files=files, - verify=self.__verify_ssl, - timeout=self.__timeout, - stream=stream_response, - headers=headers, - ) - except requests.RequestException as e: - raise CommunicationError(e) - - if raw_response: - return response - return self.__handle_response(response, raw) - - except Error as e: - raise e - - except CommunicationError as e: - raise e - - -class LastlineCommunityAPIClient(object): - """"A very basic API client providing basic functionality.""" - - def __init__(self, api_url, login_params): - """ - Instantiate the API client. - - :param str api_url: the URL to the API server - :param dict[str, str] login_params: the login parameters - """ - self._client = LastlineCommunityHTTPClient(api_url, login_params) - self._logger = logging.getLogger(__name__) - - def _post(self, module, function, params=None, data=None, files=None, fmt="JSON"): - return self._client.do_request( - method="POST", - module=module, - function=function, - params=params, - data=data, - files=files, - fmt=fmt, - ) - - def _get(self, module, function, params=None, fmt="JSON"): - return self._client.do_request( - method="GET", - module=module, - function=function, - params=params, - fmt=fmt, - ) - - def get_progress(self, uuid, analysis_instance=None): - """ - Get the completion progress of a given task. - - :param str uuid: the unique identifier of the submitted task - :param str analysis_instance: if set, defines the analysis instance to query - :rtype: dict[str, int] - :return: a dictionary like the the following: - { - "completed": 1, - "progress": 100 - } - """ - params = purge_none({"uuid": uuid, "analysis_instance": analysis_instance}) - return self._get("analysis", "get_progress", params=params) - - def get_result(self, uuid, analysis_instance=None): - """ - Get report results for a given task. - - :param str uuid: the unique identifier of the submitted task - :param str analysis_instance: if set, defines the analysis instance to query - :rtype: dict[str, any] - :return: a dictionary like the the following: - { - "completed": 1, - "progress": 100 - } - """ - params = purge_none( - { - "uuid": uuid, - "analysis_instance": analysis_instance, - "report_format": "json", - } - ) - return self._get("analysis", "get_result", params=params) - - def submit_url( - self, - url, - referer=None, - user_agent=None, - bypass_cache=False, - ): - """ - Upload an URL to be analyzed. - - :param str url: the url to analyze - :param str|None referer: the referer - :param str|None user_agent: the user agent - :param boolean bypass_cache: bypass_cache - :rtype: dict[str, any] - :return: a dictionary like the following if the analysis is already available: - { - "submission": "2019-11-17 09:33:23", - "child_tasks": [...], - "reports": [...], - "submission_timestamp": "2019-11-18 16:11:04", - "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", - "score": 0, - "analysis_subject": { - "url": "https://www.google.com" - }, - "last_submission_timestamp": "2019-11-18 16:11:04" - } - - OR the following if the analysis is still pending: - - { - "submission_timestamp": "2019-11-18 13:59:25", - "task_uuid": "f3c0ae115d51001017ff8da768fa6049", - } - """ - params = purge_none( - { - "url": url, - "bypass_cache": bypass_cache, - "referer": referer, - "user_agent": user_agent - } - ) - return self._post(module="analysis", function="submit_url", params=params) - - def submit_file( - self, - file_data, - file_name=None, - password=None, - analysis_env=None, - allow_network_traffic=True, - analysis_timeout=None, - bypass_cache=False, - ): - """ - Upload a file to be analyzed. - - :param bytes file_data: the data as a byte sequence - :param str|None file_name: if set, represents the name of the file to submit - :param str|None password: if set, use it to extract the sample - :param str|None analysis_env: if set, e.g windowsxp - :param boolean allow_network_traffic: if set to False, deny network connections - :param int|None analysis_timeout: if set limit the duration of the analysis - :param boolean bypass_cache: whether to re-process a file (requires special permissions) - :rtype: dict[str, any] - :return: a dictionary in the following form if the analysis is already available: - { - "submission": "2019-11-17 09:33:23", - "child_tasks": [...], - "reports": [...], - "submission_timestamp": "2019-11-18 16:11:04", - "task_uuid": "86097fb8e4cd00100464cb001b97ecbe", - "score": 0, - "analysis_subject": { - "url": "https://www.google.com" - }, - "last_submission_timestamp": "2019-11-18 16:11:04" - } - - OR the following if the analysis is still pending: - - { - "submission_timestamp": "2019-11-18 13:59:25", - "task_uuid": "f3c0ae115d51001017ff8da768fa6049", - } - """ - params = purge_none( - { - "filename": file_name, - "password": password, - "analysis_env": analysis_env, - "allow_network_traffic": allow_network_traffic, - "analysis_timeout": analysis_timeout, - "bypass_cache": bypass_cache, - } - ) - files = {"file": (file_name, file_data, "application/octet-stream")} - return self._post(module="analysis", function="submit_file", params=params, files=files) diff --git a/misp_modules/modules/expansion/lastline_query.py b/misp_modules/modules/expansion/lastline_query.py index 4019b92..9fdc9de 100644 --- a/misp_modules/modules/expansion/lastline_query.py +++ b/misp_modules/modules/expansion/lastline_query.py @@ -27,8 +27,6 @@ moduleinfo = { } moduleconfig = [ - "api_key", - "api_token", "username", "password", ] @@ -51,24 +49,25 @@ def handler(q=False): # Parse the init parameters try: - auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) + config = request["config"] + auth_data = lastline_api.LastlineAbstractClient.get_login_params_from_dict(config) analysis_link = request['attribute']['value'] # The API url changes based on the analysis link host name - api_url = lastline_api.get_api_url_from_link(analysis_link) + api_url = lastline_api.get_portal_url_from_task_link(analysis_link) except Exception as e: misperrors["error"] = "Error parsing configuration: {}".format(e) return misperrors # Parse the call parameters try: - task_uuid = lastline_api.get_uuid_from_link(analysis_link) + task_uuid = lastline_api.get_uuid_from_task_link(analysis_link) except (KeyError, ValueError) as e: misperrors["error"] = "Error processing input parameters: {}".format(e) return misperrors # Make the API calls try: - api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + api_client = lastline_api.PortalClient(api_url, auth_data) response = api_client.get_progress(task_uuid) if response.get("completed") != 1: raise ValueError("Analysis is not finished yet.") @@ -108,7 +107,7 @@ if __name__ == "__main__": args = parser.parse_args() c = configparser.ConfigParser() c.read(args.config_file) - a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + a = lastline_api.LastlineAbstractClient.get_login_params_from_conf(c, args.section_name) j = json.dumps( { diff --git a/misp_modules/modules/expansion/lastline_submit.py b/misp_modules/modules/expansion/lastline_submit.py index 0ae475a..1572955 100644 --- a/misp_modules/modules/expansion/lastline_submit.py +++ b/misp_modules/modules/expansion/lastline_submit.py @@ -33,13 +33,9 @@ moduleinfo = { } moduleconfig = [ - "api_url", - "api_key", + "url", "api_token", - "username", - "password", - # Module options - "bypass_cache", + "key", ] @@ -75,31 +71,31 @@ def handler(q=False): # Parse the init parameters try: - auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) - api_url = request.get("config", {}).get("api_url", lastline_api.DEFAULT_LASTLINE_API) + config = request.get("config", {}) + auth_data = lastline_api.LastlineAbstractClient.get_login_params_from_dict(config) + api_url = config.get("url", lastline_api.DEFAULT_LL_ANALYSIS_API_URL) except Exception as e: misperrors["error"] = "Error parsing configuration: {}".format(e) return misperrors # Parse the call parameters try: - bypass_cache = request.get("config", {}).get("bypass_cache", False) - call_args = {"bypass_cache": __str_to_bool(bypass_cache)} + call_args = {} if "url" in request: # URLs are text strings - api_method = lastline_api.LastlineCommunityAPIClient.submit_url + api_method = lastline_api.AnalysisClient.submit_url call_args["url"] = request.get("url") else: data = request.get("data") # Malware samples are zip-encrypted and then base64 encoded if "malware-sample" in request: - api_method = lastline_api.LastlineCommunityAPIClient.submit_file + api_method = lastline_api.AnalysisClient.submit_file call_args["file_data"] = __unzip(base64.b64decode(data), DEFAULT_ZIP_PASSWORD) call_args["file_name"] = request.get("malware-sample").split("|", 1)[0] call_args["password"] = DEFAULT_ZIP_PASSWORD # Attachments are just base64 encoded elif "attachment" in request: - api_method = lastline_api.LastlineCommunityAPIClient.submit_file + api_method = lastline_api.AnalysisClient.submit_file call_args["file_data"] = base64.b64decode(data) call_args["file_name"] = request.get("attachment") @@ -112,7 +108,7 @@ def handler(q=False): # Make the API call try: - api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + api_client = lastline_api.AnalysisClient(api_url, auth_data) response = api_method(api_client, **call_args) task_uuid = response.get("task_uuid") if not task_uuid: @@ -127,7 +123,7 @@ def handler(q=False): return misperrors # Assemble and return - analysis_link = lastline_api.get_analysis_link(api_url, task_uuid) + analysis_link = lastline_api.get_task_link(task_uuid, analysis_url=api_url) return { "results": [ @@ -152,12 +148,12 @@ if __name__ == "__main__": args = parser.parse_args() c = configparser.ConfigParser() c.read(args.config_file) - a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + a = lastline_api.LastlineAbstractClient.get_login_params_from_conf(c, args.section_name) j = json.dumps( { "config": a, - "url": "https://www.google.com", + "url": "https://www.google.exe.com", } ) print(json.dumps(handler(j), indent=4, sort_keys=True)) diff --git a/misp_modules/modules/import_mod/lastline_import.py b/misp_modules/modules/import_mod/lastline_import.py index ff26b93..ebf88d8 100644 --- a/misp_modules/modules/import_mod/lastline_import.py +++ b/misp_modules/modules/import_mod/lastline_import.py @@ -29,8 +29,6 @@ moduleinfo = { } moduleconfig = [ - "api_key", - "api_token", "username", "password", ] @@ -65,24 +63,25 @@ def handler(q=False): # Parse the init parameters try: - auth_data = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_request(request) + config = request["config"] + auth_data = lastline_api.LastlineAbstractClient.get_login_params_from_dict(config) analysis_link = request["config"]["analysis_link"] # The API url changes based on the analysis link host name - api_url = lastline_api.get_api_url_from_link(analysis_link) + api_url = lastline_api.get_portal_url_from_task_link(analysis_link) except Exception as e: misperrors["error"] = "Error parsing configuration: {}".format(e) return misperrors # Parse the call parameters try: - task_uuid = lastline_api.get_uuid_from_link(analysis_link) + task_uuid = lastline_api.get_uuid_from_task_link(analysis_link) except (KeyError, ValueError) as e: misperrors["error"] = "Error processing input parameters: {}".format(e) return misperrors # Make the API calls try: - api_client = lastline_api.LastlineCommunityAPIClient(api_url, auth_data) + api_client = lastline_api.PortalClient(api_url, auth_data) response = api_client.get_progress(task_uuid) if response.get("completed") != 1: raise ValueError("Analysis is not finished yet.") @@ -122,7 +121,7 @@ if __name__ == "__main__": args = parser.parse_args() c = configparser.ConfigParser() c.read(args.config_file) - a = lastline_api.LastlineCommunityHTTPClient.get_login_params_from_conf(c, args.section_name) + a = lastline_api.LastlineAbstractClient.get_login_params_from_conf(c, args.section_name) j = json.dumps( { From f28aaf07c433692249b43a94f7cf6de70723b9b5 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 21 Jan 2020 22:04:08 +0100 Subject: [PATCH 082/220] fix: [tests] Fixed BGP raking module test --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 879c590..ee3a906 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -99,7 +99,7 @@ class TestExpansions(unittest.TestCase): def test_bgpranking(self): query = {"module": "bgpranking", "AS": "13335"} response = self.misp_modules_post(query) - self.assertEqual(self.get_values(response)['response']['asn_description'], 'CLOUDFLARENET - Cloudflare, Inc., US') + self.assertEqual(self.get_values(response)['response']['asn_description'], 'CLOUDFLARENET, US') def test_btc_steroids(self): query = {"module": "btc_steroids", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From 04685ea63e4cf38934945a8f384d60b00bee9b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Georg=20Sch=C3=B6lly?= Date: Fri, 24 Jan 2020 14:51:10 +0100 Subject: [PATCH 083/220] joe: (1) allow users to disable PE object import (2) set 'to_ids' to False --- misp_modules/lib/joe_parser.py | 87 +++++++++++-------- .../modules/expansion/joesandbox_query.py | 28 ++++-- misp_modules/modules/import_mod/joe_import.py | 22 ++++- 3 files changed, 91 insertions(+), 46 deletions(-) diff --git a/misp_modules/lib/joe_parser.py b/misp_modules/lib/joe_parser.py index 00aa868..22a4918 100644 --- a/misp_modules/lib/joe_parser.py +++ b/misp_modules/lib/joe_parser.py @@ -51,12 +51,15 @@ signerinfo_object_mapping = {'sigissuer': ('text', 'issuer'), class JoeParser(): - def __init__(self): + def __init__(self, config): self.misp_event = MISPEvent() self.references = defaultdict(list) self.attributes = defaultdict(lambda: defaultdict(set)) self.process_references = {} + self.import_pe = config["import_pe"] + self.create_mitre_attack = config["mitre_attack"] + def parse_data(self, data): self.data = data if self.analysis_type() == "file": @@ -72,7 +75,9 @@ class JoeParser(): if self.attributes: self.handle_attributes() - self.parse_mitre_attack() + + if self.create_mitre_attack: + self.parse_mitre_attack() def build_references(self): for misp_object in self.misp_event.objects: @@ -97,12 +102,12 @@ class JoeParser(): file_object = MISPObject('file') for key, mapping in dropped_file_mapping.items(): attribute_type, object_relation = mapping - file_object.add_attribute(object_relation, **{'type': attribute_type, 'value': droppedfile[key]}) + file_object.add_attribute(object_relation, **{'type': attribute_type, 'value': droppedfile[key], 'to_ids': False}) if droppedfile['@malicious'] == 'true': - file_object.add_attribute('state', **{'type': 'text', 'value': 'Malicious'}) + file_object.add_attribute('state', **{'type': 'text', 'value': 'Malicious', 'to_ids': False}) for h in droppedfile['value']: hash_type = dropped_hash_mapping[h['@algo']] - file_object.add_attribute(hash_type, **{'type': hash_type, 'value': h['$']}) + file_object.add_attribute(hash_type, **{'type': hash_type, 'value': h['$'], 'to_ids': False}) self.misp_event.add_object(**file_object) self.references[self.process_references[(int(droppedfile['@targetid']), droppedfile['@process'])]].append({ 'referenced_uuid': file_object.uuid, @@ -132,9 +137,12 @@ class JoeParser(): for object_relation, attribute in attributes.items(): network_connection_object.add_attribute(object_relation, **attribute) network_connection_object.add_attribute('first-packet-seen', - **{'type': 'datetime', 'value': min(tuple(min(timestamp) for timestamp in data.values()))}) + **{'type': 'datetime', + 'value': min(tuple(min(timestamp) for timestamp in data.values())), + 'to_ids': False}) for protocol in data.keys(): - network_connection_object.add_attribute('layer{}-protocol'.format(protocols[protocol]), **{'type': 'text', 'value': protocol}) + network_connection_object.add_attribute('layer{}-protocol'.format(protocols[protocol]), + **{'type': 'text', 'value': protocol, 'to_ids': False}) self.misp_event.add_object(**network_connection_object) self.references[self.analysisinfo_uuid].append(dict(referenced_uuid=network_connection_object.uuid, relationship_type='initiates')) @@ -143,8 +151,8 @@ class JoeParser(): network_connection_object = MISPObject('network-connection') for object_relation, attribute in attributes.items(): network_connection_object.add_attribute(object_relation, **attribute) - network_connection_object.add_attribute('first-packet-seen', **{'type': 'datetime', 'value': min(timestamps)}) - network_connection_object.add_attribute('layer{}-protocol'.format(protocols[protocol]), **{'type': 'text', 'value': protocol}) + network_connection_object.add_attribute('first-packet-seen', **{'type': 'datetime', 'value': min(timestamps), 'to_ids': False}) + network_connection_object.add_attribute('layer{}-protocol'.format(protocols[protocol]), **{'type': 'text', 'value': protocol, 'to_ids': False}) self.misp_event.add_object(**network_connection_object) self.references[self.analysisinfo_uuid].append(dict(referenced_uuid=network_connection_object.uuid, relationship_type='initiates')) @@ -154,7 +162,8 @@ class JoeParser(): if screenshotdata: screenshotdata = screenshotdata['interesting']['$'] attribute = {'type': 'attachment', 'value': 'screenshot.jpg', - 'data': screenshotdata, 'disable_correlation': True} + 'data': screenshotdata, 'disable_correlation': True, + 'to_ids': False} self.misp_event.add_attribute(**attribute) def parse_system_behavior(self): @@ -166,9 +175,9 @@ class JoeParser(): general = process['general'] process_object = MISPObject('process') for feature, relation in process_object_fields.items(): - process_object.add_attribute(relation, **{'type': 'text', 'value': general[feature]}) + process_object.add_attribute(relation, **{'type': 'text', 'value': general[feature], 'to_ids': False}) start_time = datetime.strptime('{} {}'.format(general['date'], general['time']), '%d/%m/%Y %H:%M:%S') - process_object.add_attribute('start-time', **{'type': 'datetime', 'value': start_time}) + process_object.add_attribute('start-time', **{'type': 'datetime', 'value': start_time, 'to_ids': False}) self.misp_event.add_object(**process_object) for field, to_call in process_activities.items(): if process.get(field): @@ -203,7 +212,7 @@ class JoeParser(): url_object = MISPObject("url") self.analysisinfo_uuid = url_object.uuid - url_object.add_attribute("url", generalinfo["target"]["url"]) + url_object.add_attribute("url", generalinfo["target"]["url"], to_ids=False) self.misp_event.add_object(**url_object) def parse_fileinfo(self): @@ -213,10 +222,10 @@ class JoeParser(): self.analysisinfo_uuid = file_object.uuid for field in file_object_fields: - file_object.add_attribute(field, **{'type': field, 'value': fileinfo[field]}) + file_object.add_attribute(field, **{'type': field, 'value': fileinfo[field], 'to_ids': False}) for field, mapping in file_object_mapping.items(): attribute_type, object_relation = mapping - file_object.add_attribute(object_relation, **{'type': attribute_type, 'value': fileinfo[field]}) + file_object.add_attribute(object_relation, **{'type': attribute_type, 'value': fileinfo[field], 'to_ids': False}) arch = self.data['generalinfo']['arch'] if arch in arch_type_mapping: to_call = arch_type_mapping[arch] @@ -234,9 +243,9 @@ class JoeParser(): attribute_type = 'text' for comment, permissions in permission_lists.items(): permission_object = MISPObject('android-permission') - permission_object.add_attribute('comment', **dict(type=attribute_type, value=comment)) + permission_object.add_attribute('comment', **dict(type=attribute_type, value=comment, to_ids=False)) for permission in permissions: - permission_object.add_attribute('permission', **dict(type=attribute_type, value=permission)) + permission_object.add_attribute('permission', **dict(type=attribute_type, value=permission, to_ids=False)) self.misp_event.add_object(**permission_object) self.references[file_object.uuid].append(dict(referenced_uuid=permission_object.uuid, relationship_type='grants')) @@ -255,24 +264,24 @@ class JoeParser(): if elf.get('type'): # Haven't seen anything but EXEC yet in the files I tested attribute_value = "EXECUTABLE" if elf['type'] == "EXEC (Executable file)" else elf['type'] - elf_object.add_attribute('type', **dict(type=attribute_type, value=attribute_value)) + elf_object.add_attribute('type', **dict(type=attribute_type, value=attribute_value, to_ids=False)) for feature, relation in elf_object_mapping.items(): if elf.get(feature): - elf_object.add_attribute(relation, **dict(type=attribute_type, value=elf[feature])) + elf_object.add_attribute(relation, **dict(type=attribute_type, value=elf[feature], to_ids=False)) sections_number = len(fileinfo['sections']['section']) - elf_object.add_attribute('number-sections', **{'type': 'counter', 'value': sections_number}) + elf_object.add_attribute('number-sections', **{'type': 'counter', 'value': sections_number, 'to_ids': False}) self.misp_event.add_object(**elf_object) for section in fileinfo['sections']['section']: section_object = MISPObject('elf-section') for feature in ('name', 'type'): if section.get(feature): - section_object.add_attribute(feature, **dict(type=attribute_type, value=section[feature])) + section_object.add_attribute(feature, **dict(type=attribute_type, value=section[feature], to_ids=False)) if section.get('size'): - section_object.add_attribute(size, **dict(type=size, value=int(section['size'], 16))) + section_object.add_attribute(size, **dict(type=size, value=int(section['size'], 16), to_ids=False)) for flag in section['flagsdesc']: try: attribute_value = elf_section_flags_mapping[flag] - section_object.add_attribute('flag', **dict(type=attribute_type, value=attribute_value)) + section_object.add_attribute('flag', **dict(type=attribute_type, value=attribute_value, to_ids=False)) except KeyError: print(f'Unknown elf section flag: {flag}') continue @@ -281,6 +290,8 @@ class JoeParser(): relationship_type=relationship)) def parse_pe(self, fileinfo, file_object): + if not self.import_pe: + return try: peinfo = fileinfo['pe'] except KeyError: @@ -292,8 +303,8 @@ class JoeParser(): self.misp_event.add_object(**file_object) for field, mapping in pe_object_fields.items(): attribute_type, object_relation = mapping - pe_object.add_attribute(object_relation, **{'type': attribute_type, 'value': peinfo[field]}) - pe_object.add_attribute('compilation-timestamp', **{'type': 'datetime', 'value': int(peinfo['timestamp'].split()[0], 16)}) + pe_object.add_attribute(object_relation, **{'type': attribute_type, 'value': peinfo[field], 'to_ids': False}) + pe_object.add_attribute('compilation-timestamp', **{'type': 'datetime', 'value': int(peinfo['timestamp'].split()[0], 16), 'to_ids': False}) program_name = fileinfo['filename'] if peinfo['versions']: for feature in peinfo['versions']['version']: @@ -301,18 +312,18 @@ class JoeParser(): if name == 'InternalName': program_name = feature['value'] if name in pe_object_mapping: - pe_object.add_attribute(pe_object_mapping[name], **{'type': 'text', 'value': feature['value']}) + pe_object.add_attribute(pe_object_mapping[name], **{'type': 'text', 'value': feature['value'], 'to_ids': False}) sections_number = len(peinfo['sections']['section']) - pe_object.add_attribute('number-sections', **{'type': 'counter', 'value': sections_number}) + pe_object.add_attribute('number-sections', **{'type': 'counter', 'value': sections_number, 'to_ids': False}) signatureinfo = peinfo['signature'] if signatureinfo['signed']: signerinfo_object = MISPObject('authenticode-signerinfo') pe_object.add_reference(signerinfo_object.uuid, 'signed-by') self.misp_event.add_object(**pe_object) - signerinfo_object.add_attribute('program-name', **{'type': 'text', 'value': program_name}) + signerinfo_object.add_attribute('program-name', **{'type': 'text', 'value': program_name, 'to_ids': False}) for feature, mapping in signerinfo_object_mapping.items(): attribute_type, object_relation = mapping - signerinfo_object.add_attribute(object_relation, **{'type': attribute_type, 'value': signatureinfo[feature]}) + signerinfo_object.add_attribute(object_relation, **{'type': attribute_type, 'value': signatureinfo[feature], 'to_ids': False}) self.misp_event.add_object(**signerinfo_object) else: self.misp_event.add_object(**pe_object) @@ -327,7 +338,7 @@ class JoeParser(): for feature, mapping in pe_section_object_mapping.items(): if section.get(feature): attribute_type, object_relation = mapping - section_object.add_attribute(object_relation, **{'type': attribute_type, 'value': section[feature]}) + section_object.add_attribute(object_relation, **{'type': attribute_type, 'value': section[feature], 'to_ids': False}) return section_object def parse_network_interactions(self): @@ -339,13 +350,13 @@ class JoeParser(): for key, mapping in domain_object_mapping.items(): attribute_type, object_relation = mapping domain_object.add_attribute(object_relation, - **{'type': attribute_type, 'value': domain[key]}) + **{'type': attribute_type, 'value': domain[key], 'to_ids': False}) self.misp_event.add_object(**domain_object) reference = dict(referenced_uuid=domain_object.uuid, relationship_type='contacts') self.add_process_reference(domain['@targetid'], domain['@currentpath'], reference) else: attribute = MISPAttribute() - attribute.from_dict(**{'type': 'domain', 'value': domain['@name']}) + attribute.from_dict(**{'type': 'domain', 'value': domain['@name'], 'to_ids': False}) self.misp_event.add_attribute(**attribute) reference = dict(referenced_uuid=attribute.uuid, relationship_type='contacts') self.add_process_reference(domain['@targetid'], domain['@currentpath'], reference) @@ -353,7 +364,7 @@ class JoeParser(): if ipinfo: for ip in ipinfo['ip']: attribute = MISPAttribute() - attribute.from_dict(**{'type': 'ip-dst', 'value': ip['@ip']}) + attribute.from_dict(**{'type': 'ip-dst', 'value': ip['@ip'], 'to_ids': False}) self.misp_event.add_attribute(**attribute) reference = dict(referenced_uuid=attribute.uuid, relationship_type='contacts') self.add_process_reference(ip['@targetid'], ip['@currentpath'], reference) @@ -363,7 +374,7 @@ class JoeParser(): target_id = int(url['@targetid']) current_path = url['@currentpath'] attribute = MISPAttribute() - attribute_dict = {'type': 'url', 'value': url['@name']} + attribute_dict = {'type': 'url', 'value': url['@name'], 'to_ids': False} if target_id != -1 and current_path != 'unknown': self.references[self.process_references[(target_id, current_path)]].append({ 'referenced_uuid': attribute.uuid, @@ -384,8 +395,8 @@ class JoeParser(): registry_key = MISPObject('registry-key') for field, mapping in regkey_object_mapping.items(): attribute_type, object_relation = mapping - registry_key.add_attribute(object_relation, **{'type': attribute_type, 'value': call[field]}) - registry_key.add_attribute('data-type', **{'type': 'text', 'value': 'REG_{}'.format(call['type'].upper())}) + registry_key.add_attribute(object_relation, **{'type': attribute_type, 'value': call[field], 'to_ids': False}) + registry_key.add_attribute('data-type', **{'type': 'text', 'value': 'REG_{}'.format(call['type'].upper()), 'to_ids': False}) self.misp_event.add_object(**registry_key) self.references[process_uuid].append(dict(referenced_uuid=registry_key.uuid, relationship_type=relationship)) @@ -398,7 +409,7 @@ class JoeParser(): def create_attribute(self, attribute_type, attribute_value): attribute = MISPAttribute() - attribute.from_dict(**{'type': attribute_type, 'value': attribute_value}) + attribute.from_dict(**{'type': attribute_type, 'value': attribute_value, 'to_ids': False}) self.misp_event.add_attribute(**attribute) return attribute.uuid @@ -419,5 +430,5 @@ class JoeParser(): attributes = {} for field, value in zip(network_behavior_fields, connection): attribute_type, object_relation = network_connection_object_mapping[field] - attributes[object_relation] = {'type': attribute_type, 'value': value} + attributes[object_relation] = {'type': attribute_type, 'value': value, 'to_ids': False} return attributes diff --git a/misp_modules/modules/expansion/joesandbox_query.py b/misp_modules/modules/expansion/joesandbox_query.py index dce63ea..1ace259 100644 --- a/misp_modules/modules/expansion/joesandbox_query.py +++ b/misp_modules/modules/expansion/joesandbox_query.py @@ -4,12 +4,13 @@ import json from joe_parser import JoeParser misperrors = {'error': 'Error'} -mispattributes = {'input': ['link'], 'format': 'misp_standard'} -moduleinfo = {'version': '0.1', 'author': 'Christian Studer', +inputSource = ['link'] + +moduleinfo = {'version': '0.2', 'author': 'Christian Studer', 'description': 'Query Joe Sandbox API with a report URL to get the parsed data.', 'module-type': ['expansion']} -moduleconfig = ['apiurl', 'apikey'] +moduleconfig = ['apiurl', 'apikey', 'import_pe', 'import_mitre_attack'] def handler(q=False): @@ -18,6 +19,11 @@ def handler(q=False): request = json.loads(q) apiurl = request['config'].get('apiurl') or 'https://jbxcloud.joesecurity.org/api' apikey = request['config'].get('apikey') + parser_config = { + "import_pe": request["config"].get('import_pe', "false") == "true", + "mitre_attack": request["config"].get('import_mitre_attack', "false") == "true", + } + if not apikey: return {'error': 'No API key provided'} @@ -41,7 +47,7 @@ def handler(q=False): analysis_webid = joe_info['most_relevant_analysis']['webid'] - joe_parser = JoeParser() + joe_parser = JoeParser(parser_config) joe_data = json.loads(joe.analysis_download(analysis_webid, 'jsonfixed')[1]) joe_parser.parse_data(joe_data['analysis']) joe_parser.finalize_results() @@ -50,7 +56,19 @@ def handler(q=False): def introspection(): - return mispattributes + modulesetup = {} + try: + userConfig + modulesetup['userConfig'] = userConfig + except NameError: + pass + try: + inputSource + modulesetup['input'] = inputSource + except NameError: + pass + modulesetup['format'] = 'misp_standard' + return modulesetup def version(): diff --git a/misp_modules/modules/import_mod/joe_import.py b/misp_modules/modules/import_mod/joe_import.py index d1c4d19..fbe7385 100644 --- a/misp_modules/modules/import_mod/joe_import.py +++ b/misp_modules/modules/import_mod/joe_import.py @@ -4,10 +4,20 @@ import json from joe_parser import JoeParser misperrors = {'error': 'Error'} -userConfig = {} +userConfig = { + "Import PE": { + "type": "Boolean", + "message": "Import PE Information", + }, + "Mitre Att&ck" : { + "type": "Boolean", + "message": "Import Mitre Att&ck techniques", + }, +} + inputSource = ['file'] -moduleinfo = {'version': '0.1', 'author': 'Christian Studer', +moduleinfo = {'version': '0.2', 'author': 'Christian Studer', 'description': 'Import for Joe Sandbox JSON reports', 'module-type': ['import']} @@ -18,10 +28,16 @@ def handler(q=False): if q is False: return False q = json.loads(q) + config = { + "import_pe": bool(int(q["config"]["Import PE"])), + "mitre_attack": bool(int(q["config"]["Mitre Att&ck"])), + } + data = base64.b64decode(q.get('data')).decode('utf-8') if not data: return json.dumps({'success': 0}) - joe_parser = JoeParser() + + joe_parser = JoeParser(config) joe_parser.parse_data(json.loads(data)['analysis']) joe_parser.finalize_results() return {'results': joe_parser.results} From b2c8f79220855e41db11d28248b01a06de8ed8c5 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 24 Jan 2020 15:17:35 +0100 Subject: [PATCH 084/220] fix: Making pep8 happy --- misp_modules/modules/import_mod/joe_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/import_mod/joe_import.py b/misp_modules/modules/import_mod/joe_import.py index fbe7385..0753167 100644 --- a/misp_modules/modules/import_mod/joe_import.py +++ b/misp_modules/modules/import_mod/joe_import.py @@ -9,7 +9,7 @@ userConfig = { "type": "Boolean", "message": "Import PE Information", }, - "Mitre Att&ck" : { + "Mitre Att&ck": { "type": "Boolean", "message": "Import Mitre Att&ck techniques", }, From 8f9940200beccd11b64b8505106e312ade227f9b Mon Sep 17 00:00:00 2001 From: Hendrik Date: Mon, 27 Jan 2020 07:43:46 +0100 Subject: [PATCH 085/220] Lastline verify_ssl option Helps people with on-prem boxes --- misp_modules/modules/expansion/lastline_query.py | 3 ++- misp_modules/modules/import_mod/lastline_import.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/lastline_query.py b/misp_modules/modules/expansion/lastline_query.py index 9fdc9de..4ce4e47 100644 --- a/misp_modules/modules/expansion/lastline_query.py +++ b/misp_modules/modules/expansion/lastline_query.py @@ -29,6 +29,7 @@ moduleinfo = { moduleconfig = [ "username", "password", + "verify_ssl", ] @@ -67,7 +68,7 @@ def handler(q=False): # Make the API calls try: - api_client = lastline_api.PortalClient(api_url, auth_data) + api_client = lastline_api.PortalClient(api_url, auth_data, verify_ssl=config.get('verify_ssl', True).lower() in ("true")) response = api_client.get_progress(task_uuid) if response.get("completed") != 1: raise ValueError("Analysis is not finished yet.") diff --git a/misp_modules/modules/import_mod/lastline_import.py b/misp_modules/modules/import_mod/lastline_import.py index ebf88d8..37f6249 100644 --- a/misp_modules/modules/import_mod/lastline_import.py +++ b/misp_modules/modules/import_mod/lastline_import.py @@ -31,6 +31,7 @@ moduleinfo = { moduleconfig = [ "username", "password", + "verify_ssl", ] @@ -81,7 +82,7 @@ def handler(q=False): # Make the API calls try: - api_client = lastline_api.PortalClient(api_url, auth_data) + api_client = lastline_api.PortalClient(api_url, auth_data, verify_ssl=config.get('verify_ssl', True).lower() in ("true")) response = api_client.get_progress(task_uuid) if response.get("completed") != 1: raise ValueError("Analysis is not finished yet.") From acdc4b9d030772f92b2e4c2705b3a977c7a9da77 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 7 Feb 2020 12:20:12 +0100 Subject: [PATCH 086/220] fix: [VT] Disable SHA512 query for VT --- misp_modules/modules/expansion/virustotal.py | 7 +++---- misp_modules/modules/expansion/virustotal_public.py | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index 77a99a2..f47a2e3 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -3,12 +3,12 @@ import json import requests misperrors = {'error': 'Error'} -mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "sha512", "url"], +mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "url"], 'format': 'misp_standard'} # possible module-types: 'expansion', 'hover' or both moduleinfo = {'version': '4', 'author': 'Hannah Ward', - 'description': 'Get information from virustotal', + 'description': 'Get information from VirusTotal', 'module-type': ['expansion']} # config fields that your code expects from the site admin @@ -25,8 +25,7 @@ class VirusTotalParser(object): self.input_types_mapping = {'ip-src': self.parse_ip, 'ip-dst': self.parse_ip, 'domain': self.parse_domain, 'hostname': self.parse_domain, 'md5': self.parse_hash, 'sha1': self.parse_hash, - 'sha256': self.parse_hash, 'sha512': self.parse_hash, - 'url': self.parse_url} + 'sha256': self.parse_hash, 'url': self.parse_url} def query_api(self, attribute): self.attribute = MISPAttribute() diff --git a/misp_modules/modules/expansion/virustotal_public.py b/misp_modules/modules/expansion/virustotal_public.py index f31855e..e7c2e96 100644 --- a/misp_modules/modules/expansion/virustotal_public.py +++ b/misp_modules/modules/expansion/virustotal_public.py @@ -3,10 +3,10 @@ import json import requests misperrors = {'error': 'Error'} -mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "sha512", "url"], +mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "url"], 'format': 'misp_standard'} moduleinfo = {'version': '1', 'author': 'Christian Studer', - 'description': 'Get information from virustotal public API v2.', + 'description': 'Get information from VirusTotal public API v2.', 'module-type': ['expansion', 'hover']} moduleconfig = ['apikey'] @@ -155,7 +155,7 @@ ip = ('ip', IpQuery) file = ('resource', HashQuery) misp_type_mapping = {'domain': domain, 'hostname': domain, 'ip-src': ip, 'ip-dst': ip, 'md5': file, 'sha1': file, 'sha256': file, - 'sha512': file, 'url': ('resource', UrlQuery)} + 'url': ('resource', UrlQuery)} def parse_error(status_code): From 4e7192f7352ae339e88a18a77e0e359d9cb53e1b Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 21:21:39 -0600 Subject: [PATCH 087/220] Added GeoIP City and GeoIP ASN Info --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index af78ca5..03adc27 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [EUPI](misp_modules/modules/expansion/eupi.py) - a hover and expansion module to get information about an URL from the [Phishing Initiative project](https://phishing-initiative.eu/?lang=en). * [Farsight DNSDB Passive DNS](misp_modules/modules/expansion/farsight_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. * [GeoIP](misp_modules/modules/expansion/geoip_country.py) - a hover and expansion module to get GeoIP information from geolite/maxmind. +* [GeoIP_City](misp_modules/modules/expansion/geoip_city.py) - a hover and expansion module to get GeoIP City information from geolite/maxmind. +* [GeoIP_ASN](misp_modules/modules/expansion/geoip_asn.py) - a hover and expansion module to get GeoIP ASN information from geolite/maxmind. * [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. * [hashdd](misp_modules/modules/expansion/hashdd.py) - a hover module to check file hashes against [hashdd.com](http://www.hashdd.com) including NSLR dataset. * [hibp](misp_modules/modules/expansion/hibp.py) - a hover module to lookup against Have I Been Pwned? From 7a3f9a422d9fd5767634be95a4e0443b8ea5bf9d Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 21:28:41 -0600 Subject: [PATCH 088/220] Added GeoIP_City Enrichment module --- misp_modules/modules/expansion/geoip_city.py | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 misp_modules/modules/expansion/geoip_city.py diff --git a/misp_modules/modules/expansion/geoip_city.py b/misp_modules/modules/expansion/geoip_city.py new file mode 100644 index 0000000..9c9f847 --- /dev/null +++ b/misp_modules/modules/expansion/geoip_city.py @@ -0,0 +1,64 @@ +import json +import geoip2.database +import sys +import logging + +log = logging.getLogger('geoip_city') +log.setLevel(logging.DEBUG) +ch = logging.StreamHandler(sys.stdout) +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +log.addHandler(ch) + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['ip-src', 'ip-dst', 'domain|ip'], 'output': ['freetext']} +moduleconfig = ['local_geolite_db'] +# possible module-types: 'expansion', 'hover' or both +moduleinfo = {'version': '0.1', 'author': 'GlennHD', + 'description': 'Query a local copy of the Maxmind Geolite City database (MMDB format)', + 'module-type': ['expansion', 'hover']} + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + + if not request.get('config') or not request['config'].get('local_geolite_db'): + return {'error': 'Please specify the path of your local copy of Maxminds Geolite database'} + path_to_geolite = request['config']['local_geolite_db'] + + if request.get('ip-dst'): + toquery = request['ip-dst'] + elif request.get('ip-src'): + toquery = request['ip-src'] + elif request.get('domain|ip'): + toquery = request['domain|ip'].split('|')[1] + else: + return False + + try: + reader = geoip2.database.Reader(path_to_geolite) + except FileNotFoundError: + return {'error': f'Unable to locate the GeoLite database you specified ({path_to_geolite}).'} + log.debug(toquery) + try: + answer = reader.city(toquery) + stringmap = 'Continent=' + str(answer.continent.name) + ', Country=' + str(answer.country.name) + ', Subdivision=' + str(answer.subdivisions.most_specific.name) + ', City=' + str(answer.city.name) + + except Exception as e: + misperrors['error'] = f"GeoIP resolving error: {e}" + return misperrors + + r = {'results': [{'types': mispattributes['output'], 'values': stringmap}]} + + return r + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 0b9b6c4f4164b306cbe2d986a2829857e4b307d3 Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 21:29:40 -0600 Subject: [PATCH 089/220] Added GeoIP_ASN Enrichment module --- misp_modules/modules/expansion/geoip_asn.py | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 misp_modules/modules/expansion/geoip_asn.py diff --git a/misp_modules/modules/expansion/geoip_asn.py b/misp_modules/modules/expansion/geoip_asn.py new file mode 100644 index 0000000..b7fa973 --- /dev/null +++ b/misp_modules/modules/expansion/geoip_asn.py @@ -0,0 +1,63 @@ +import json +import geoip2.database +import sys +import logging + +log = logging.getLogger('geoip_asn') +log.setLevel(logging.DEBUG) +ch = logging.StreamHandler(sys.stdout) +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +log.addHandler(ch) + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['ip-src', 'ip-dst', 'domain|ip'], 'output': ['freetext']} +moduleconfig = ['local_geolite_db'] +# possible module-types: 'expansion', 'hover' or both +moduleinfo = {'version': '0.1', 'author': 'GlennHD', + 'description': 'Query a local copy of the Maxmind Geolite ASN database (MMDB format)', + 'module-type': ['expansion', 'hover']} + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + + if not request.get('config') or not request['config'].get('local_geolite_db'): + return {'error': 'Please specify the path of your local copy of the Maxmind Geolite ASN database'} + path_to_geolite = request['config']['local_geolite_db'] + + if request.get('ip-dst'): + toquery = request['ip-dst'] + elif request.get('ip-src'): + toquery = request['ip-src'] + elif request.get('domain|ip'): + toquery = request['domain|ip'].split('|')[1] + else: + return False + + try: + reader = geoip2.database.Reader(path_to_geolite) + except FileNotFoundError: + return {'error': f'Unable to locate the GeoLite database you specified ({path_to_geolite}).'} + log.debug(toquery) + try: + answer = reader.asn(toquery) + stringmap = 'ASN=' + str(answer.autonomous_system_number) + ', AS Org=' + str(answer.autonomous_system_organization) + except Exception as e: + misperrors['error'] = f"GeoIP resolving error: {e}" + return misperrors + + r = {'results': [{'types': mispattributes['output'], 'values': stringmap}]} + + return r + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 46f0f410e79be952bce3e58f289fdda5c83f7e80 Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 21:31:41 -0600 Subject: [PATCH 090/220] Added geoip_asn and geoip_city to load --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 12c2ab6..458611f 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -6,7 +6,7 @@ sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/') __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'circl_passivessl', 'countrycode', 'cve', 'cve_advanced', 'dns', 'btc_steroids', 'domaintools', 'eupi', 'eql', 'farsight_passivedns', 'ipasn', 'passivetotal', 'sourcecache', 'virustotal', - 'whois', 'shodan', 'reversedns', 'geoip_country', 'wiki', 'iprep', + 'whois', 'shodan', 'reversedns', 'geoip_asn', 'geoip_city', 'geoip_country', 'wiki', 'iprep', 'threatminer', 'otx', 'threatcrowd', 'vulndb', 'crowdstrike_falcon', 'yara_syntax_validator', 'hashdd', 'onyphe', 'onyphe_full', 'rbl', 'xforceexchange', 'sigma_syntax_validator', 'stix2_pattern_syntax_validator', From bdb4185a0ada4c815ea11b29d1e4a5d08e381694 Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 23:48:20 -0600 Subject: [PATCH 091/220] Update geoip_city.py --- misp_modules/modules/expansion/geoip_city.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misp_modules/modules/expansion/geoip_city.py b/misp_modules/modules/expansion/geoip_city.py index 9c9f847..01c0627 100644 --- a/misp_modules/modules/expansion/geoip_city.py +++ b/misp_modules/modules/expansion/geoip_city.py @@ -19,6 +19,7 @@ moduleinfo = {'version': '0.1', 'author': 'GlennHD', 'description': 'Query a local copy of the Maxmind Geolite City database (MMDB format)', 'module-type': ['expansion', 'hover']} + def handler(q=False): if q is False: return False From 0ed0ceab9d55835c78b51ee68b2c41f6f4e5dde3 Mon Sep 17 00:00:00 2001 From: GlennHD Date: Wed, 12 Feb 2020 23:48:38 -0600 Subject: [PATCH 092/220] Update geoip_asn.py --- misp_modules/modules/expansion/geoip_asn.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misp_modules/modules/expansion/geoip_asn.py b/misp_modules/modules/expansion/geoip_asn.py index b7fa973..95d7ee7 100644 --- a/misp_modules/modules/expansion/geoip_asn.py +++ b/misp_modules/modules/expansion/geoip_asn.py @@ -19,6 +19,7 @@ moduleinfo = {'version': '0.1', 'author': 'GlennHD', 'description': 'Query a local copy of the Maxmind Geolite ASN database (MMDB format)', 'module-type': ['expansion', 'hover']} + def handler(q=False): if q is False: return False From 27717c040029e2d849f0e2c62a0240bdb18ae1d5 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 13 Feb 2020 11:40:22 +0100 Subject: [PATCH 093/220] fix: Making the module config available so the module works --- misp_modules/modules/expansion/geoip_country.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/geoip_country.py b/misp_modules/modules/expansion/geoip_country.py index 8ba012b..d28e570 100644 --- a/misp_modules/modules/expansion/geoip_country.py +++ b/misp_modules/modules/expansion/geoip_country.py @@ -59,5 +59,5 @@ def introspection(): def version(): - # moduleinfo['config'] = moduleconfig + moduleinfo['config'] = moduleconfig return moduleinfo From df3a6986ea5f2ba4ac73656037115e62c9c8c6d5 Mon Sep 17 00:00:00 2001 From: Mathilde Oun et Vincent Gindt Date: Fri, 21 Feb 2020 12:05:41 +0100 Subject: [PATCH 094/220] =?UTF-8?q?Rendu=20projet=20master2=20s=C3=A9curit?= =?UTF-8?q?=C3=A9=20par=20Mathilde=20OUN=20et=20Vincent=20GINDT=20//=20Nou?= =?UTF-8?q?veau=20module=20misp=20de=20recherche=20google=20sur=20les=20ur?= =?UTF-8?q?ls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/expansion/google_search.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 misp_modules/modules/expansion/google_search.py diff --git a/misp_modules/modules/expansion/google_search.py b/misp_modules/modules/expansion/google_search.py new file mode 100644 index 0000000..2e8f2a8 --- /dev/null +++ b/misp_modules/modules/expansion/google_search.py @@ -0,0 +1,37 @@ +import json +import requests +try: + from google import google +except ImportError: + print("GoogleAPI not installed. Command : pip install git+https://github.com/abenassi/Google-Search-API") + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['url'], 'output': ['text']} +moduleinfo = {'author': 'Oun & Gindt', 'description': 'An expansion hover module to expand google search information about an URL', 'module-type': ['hover']} + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if request.get('url'): + toquery = request['url'] + else: + misperrors['error'] = "Unsupported attributes type" + return misperrors + + num_page = 1 + res = "" + search_results = google.search(request['url'], num_page) + for i in range(3): + res += "("+str(i+1)+")" + '\t' + res += json.dumps(search_results[i].description, ensure_ascii=False) + res += '\n\n' + return {'results': [{'types': mispattributes['output'], 'values':res}]} + +def introspection(): + return mispattributes + + +def version(): + return moduleinfo From f5af7faace15b9232948ef23f23408eab10123ab Mon Sep 17 00:00:00 2001 From: Sean Whalen <44679+seanthegeek@users.noreply.github.com> Date: Sat, 22 Feb 2020 19:44:31 -0500 Subject: [PATCH 095/220] Create __init__.py --- misp_modules/modules/expansion/_ransomcoindb/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 misp_modules/modules/expansion/_ransomcoindb/__init__.py diff --git a/misp_modules/modules/expansion/_ransomcoindb/__init__.py b/misp_modules/modules/expansion/_ransomcoindb/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/misp_modules/modules/expansion/_ransomcoindb/__init__.py @@ -0,0 +1 @@ + From 42dffa7291d84de2ea291ddec06b819aa5a5cee9 Mon Sep 17 00:00:00 2001 From: Sean Whalen <44679+seanthegeek@users.noreply.github.com> Date: Sun, 23 Feb 2020 15:24:18 -0500 Subject: [PATCH 096/220] Install cmake to build faup --- docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 8ac6d9f..3d5efa7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -21,6 +21,7 @@ RUN set -eu \ libzbar0 \ libzbar-dev \ libfuzzy-dev \ + cmake \ ;apt-get -y autoremove \ ;apt-get -y clean \ ;rm -rf /var/lib/apt/lists/* \ From 180985f89cf8e9effcfb28399e76beafff3814f9 Mon Sep 17 00:00:00 2001 From: Sean Whalen <44679+seanthegeek@users.noreply.github.com> Date: Sun, 23 Feb 2020 15:34:02 -0500 Subject: [PATCH 097/220] Revert change inteded for other patch --- docker/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3d5efa7..8ac6d9f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -21,7 +21,6 @@ RUN set -eu \ libzbar0 \ libzbar-dev \ libfuzzy-dev \ - cmake \ ;apt-get -y autoremove \ ;apt-get -y clean \ ;rm -rf /var/lib/apt/lists/* \ From dea42d39297b237d7b16ccbbcd2182af5d1a7561 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 25 Feb 2020 15:22:06 +0100 Subject: [PATCH 098/220] chg: Catching missing config issue --- misp_modules/modules/expansion/ransomcoindb.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index 3bac983..2b9b566 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -26,6 +26,8 @@ def handler(q=False): return False q = json.loads(q) + if "config" not in q or "api-key" not in q["config"]: + return {"error": "Ransomcoindb API key is missing"} api_key = q["config"]["api-key"] r = {"results": []} From f9f3db84680dfa167ef9c4f35261d97499d5b052 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 25 Feb 2020 15:26:52 +0100 Subject: [PATCH 099/220] chg: Quick ransomdncoin test just to make sure the module loads - I do not have any api key right now, so the test should just reach the error --- tests/test_expansions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index ee3a906..801769a 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -363,6 +363,15 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_values(response), '1GXZ6v7FZzYBEnoRaG77SJxhu7QkvQmFuh') + def test_ransomcoindb(self): + query = {"module": "ransomcoindb", + "attributes": {"type": "btc", + "value": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}} + if 'ransomcoindb' not in self.configs: + response = self.misp_modules_post(query) + self.assertEqual(self.get_errors(response), "Ransomcoindb API key is missing") + def test_rbl(self): query = {"module": "rbl", "ip-src": "8.8.8.8"} response = self.misp_modules_post(query) From c9c6f69bd432e2a98129d02c3ad2b125fb321a80 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 26 Feb 2020 11:59:14 +0100 Subject: [PATCH 100/220] fix: Making pep8 happy --- .../expansion/_ransomcoindb/__init__.py | 1 - .../modules/expansion/google_search.py | 40 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/misp_modules/modules/expansion/_ransomcoindb/__init__.py b/misp_modules/modules/expansion/_ransomcoindb/__init__.py index 8b13789..e69de29 100644 --- a/misp_modules/modules/expansion/_ransomcoindb/__init__.py +++ b/misp_modules/modules/expansion/_ransomcoindb/__init__.py @@ -1 +0,0 @@ - diff --git a/misp_modules/modules/expansion/google_search.py b/misp_modules/modules/expansion/google_search.py index 2e8f2a8..067edaf 100644 --- a/misp_modules/modules/expansion/google_search.py +++ b/misp_modules/modules/expansion/google_search.py @@ -1,37 +1,35 @@ import json import requests try: - from google import google + from google import google except ImportError: - print("GoogleAPI not installed. Command : pip install git+https://github.com/abenassi/Google-Search-API") + print("GoogleAPI not installed. Command : pip install git+https://github.com/abenassi/Google-Search-API") misperrors = {'error': 'Error'} mispattributes = {'input': ['url'], 'output': ['text']} -moduleinfo = {'author': 'Oun & Gindt', 'description': 'An expansion hover module to expand google search information about an URL', 'module-type': ['hover']} +moduleinfo = {'author': 'Oun & Gindt', 'module-type': ['hover'], + 'description': 'An expansion hover module to expand google search information about an URL'} def handler(q=False): - if q is False: - return False - request = json.loads(q) - if request.get('url'): - toquery = request['url'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors + if q is False: + return False + request = json.loads(q) + if not request.get('url'): + return {'error': "Unsupported attributes type"} + num_page = 1 + res = "" + search_results = google.search(request['url'], num_page) + for i in range(3): + res += "("+str(i+1)+")" + '\t' + res += json.dumps(search_results[i].description, ensure_ascii=False) + res += '\n\n' + return {'results': [{'types': mispattributes['output'], 'values':res}]} - num_page = 1 - res = "" - search_results = google.search(request['url'], num_page) - for i in range(3): - res += "("+str(i+1)+")" + '\t' - res += json.dumps(search_results[i].description, ensure_ascii=False) - res += '\n\n' - return {'results': [{'types': mispattributes['output'], 'values':res}]} def introspection(): - return mispattributes + return mispattributes def version(): - return moduleinfo + return moduleinfo From cda5004a0ddf1aa167fd5ad01f5b2f4ea2bcb6e5 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 26 Feb 2020 14:18:09 +0100 Subject: [PATCH 101/220] fix: Removed unused import --- misp_modules/modules/expansion/google_search.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misp_modules/modules/expansion/google_search.py b/misp_modules/modules/expansion/google_search.py index 067edaf..b7b4e7a 100644 --- a/misp_modules/modules/expansion/google_search.py +++ b/misp_modules/modules/expansion/google_search.py @@ -1,5 +1,4 @@ import json -import requests try: from google import google except ImportError: From a32685df8af9c9ae50db9bf214f8759a44084b5c Mon Sep 17 00:00:00 2001 From: bennyv Date: Wed, 4 Mar 2020 09:52:55 +1100 Subject: [PATCH 102/220] Initial Build of SOPHOSLabs Intelix Product --- misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/sophoslabs_intelix.py | 125 ++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/sophoslabs_intelix.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 458611f..8eb0a92 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -16,4 +16,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', - 'lastline_query', 'lastline_submit'] + 'lastline_query', 'lastline_submit', 'sophoslabs_intelix'] diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py new file mode 100644 index 0000000..4cc65ca --- /dev/null +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -0,0 +1,125 @@ +from pymisp import MISPAttribute, MISPEvent, MISPObject +import json +import requests +import base64 +from urllib.parse import quote + +moduleinfo = {'version': '1.0', + 'author': 'Ben Verschaeren', + 'description': 'SOPHOSLabs Intelix Integration', + 'module-type': ['expansion']} + +moduleconfig = ['client_id', 'client_secret'] + +misperrors = {'error': 'Error'} + +misp_types_in = ['sha256', 'ip', 'ip-src', 'ip-dst', 'uri', 'url', 'domain', 'hostname'] + +mispattributes = {'input': misp_types_in, + 'format': 'misp_standard'} + +class SophosLabsApi(): + def __init__(self, client_id, client_secret): + self.misp_event = MISPEvent() + self.client_id = client_id + self.client_secret= client_secret + self.authToken = f"{self.client_id}:{self.client_secret}" + self.baseurl = 'de.api.labs.sophos.com' + d = {'grant_type': 'client_credentials'} + h = {'Authorization': f"Basic {base64.b64encode(self.authToken.encode('UTF-8')).decode('ascii')}",\ + 'Content-Type': 'application/x-www-form-urlencoded'} + r = requests.post('https://api.labs.sophos.com/oauth2/token', headers=h, data=d) + if r.status_code == 200: + j = json.loads(r.text) + self.accessToken = j['access_token'] + + def get_result(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + + def hash_lookup(self, filehash): + sophos_object = MISPObject('SOPHOSLabs Intelix SHA256 Report') + h = {"Authorization": f"{self.accessToken}"} + r = requests.get(f"https://{self.baseurl}/lookup/files/v1/{filehash}", headers=h) + if r.status_code == 200: + j = json.loads(r.text) + if 'reputationScore' in j: + sophos_object.add_attribute('Reputation Score', type='text', value=j['reputationScore']) + if 0 <= j['reputationScore'] <= 19: + sophos_object.add_attribute('Decision', type='text', value='This file is malicious') + if 20 <= j['reputationScore'] <= 29: + sophos_object.add_attribute('Decision', type='text', value='This file is potentially unwanted') + if 30 <= j['reputationScore'] <= 69: + sophos_object.add_attribute('Decision', type='text', value='This file is unknown and suspicious') + if 70 <= j['reputationScore'] <= 100: + sophos_object.add_attribute('Decision', type='text', value='This file is known good') + if 'detectionName' in j: + sophos_object.add_attribute('Detection Name', type='text', value=j['detectionName']) + else: + sophos_object.add_attribute('Detection Name', type='text', value='No name associated with this IoC') + self.misp_event.add_object(**sophos_object) + + def ip_lookup(self, ip): + sophos_object = MISPObject('SOPHOSLabs Intelix IP Category Lookup') + h = {"Authorization": f"{self.accessToken}"} + r = requests.get(f"https://{self.baseurl}/lookup/ips/v1/{ip}", headers=h) + if r.status_code == 200: + j = json.loads(r.text) + if 'category' in j: + for c in j['category']: + sophos_object.add_attribute('IP Address Categorisation', type='text', value=c) + else: + sophos_object.add_attribute('IP Address Categorisation', type='text', value='No category assocaited with IoC') + self.misp_event.add_object(**sophos_object) + + def url_lookup(self, url): + sophos_object = MISPObject('SOPHOSLabs Intelix URL Lookup') + h = {"Authorization": f"{self.accessToken}"} + r = requests.get(f"https://{self.baseurl}/lookup/urls/v1/{quote(url, safe='')}", headers=h) + if r.status_code == 200: + j = json.loads(r.text) + if 'productivityCategory' in j: + sophos_object.add_attribute('URL Categorisation', type='text', value=j['productivityCategory']) + else: + sophos_object.add_attribute('URL Categorisation', type='text', value='No category assocaited with IoC') + + if 'riskLevel' in j: + sophos_object.add_attribute('URL Risk Level', type='text', value=j['riskLevel']) + else: + sophos_object.add_attribute('URL Risk Level', type='text', value='No risk level associated with IoC') + + if 'securityCategory' in j: + sophos_object.add_attribute('URL Security Category', type='text', value=j['securityCategory']) + else: + sophos_object.add_attribute('URL Security Category', type='text', value='No Security Category associated with IoC') + self.misp_event.add_object(**sophos_object) + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if request['config']['client_id'] is None or request['config']['client_secret'] is None: + misperrors['error'] = "Missing client_id or client_secret value for SOPHOSLabs Intelix. \ + It's free to Sign Up here https://aws.amazon.com/marketplace/pp/B07SLZPMCS." + return misperrors + else: + client = SophosLabsApi(request['config']['client_id'], request['config']['client_secret']) + if request['attribute']['type'] == "sha256": + client.hash_lookup(request['attribute']['value1']) + if request['attribute']['type'] in ['ip-dst', 'ip-src', 'ip']: + client.ip_lookup(request["attribute"]["value1"]) + if request['attribute']['type'] in ['uri', 'url', 'domain', 'hostname']: + client.url_lookup(request["attribute"]["value1"]) + return client.get_result() + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo + From 277f56e088c5c63e16d86da7f74aa0638e5821de Mon Sep 17 00:00:00 2001 From: bennyv Date: Wed, 4 Mar 2020 10:39:35 +1100 Subject: [PATCH 103/220] Updated the README.md for SOPHOSLabs Intelix --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 03adc27..f4c3156 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [shodan](misp_modules/modules/expansion/shodan.py) - a minimal [shodan](https://www.shodan.io/) expansion module. * [Sigma queries](misp_modules/modules/expansion/sigma_queries.py) - Experimental expansion module querying a sigma rule to convert it into all the available SIEM signatures. * [Sigma syntax validator](misp_modules/modules/expansion/sigma_syntax_validator.py) - Sigma syntax validator. +* [SOPHOSLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SOPHOSLabs Intelix is an API for Threat Intelligence and Analysis (Free tier)[SOPHOSLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS) * [sourcecache](misp_modules/modules/expansion/sourcecache.py) - a module to cache a specific link from a MISP instance. * [STIX2 pattern syntax validator](misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax. * [ThreatCrowd](misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/). From 4771a5177de39ee0f8a04f72091729b35663c73c Mon Sep 17 00:00:00 2001 From: bennyv Date: Wed, 4 Mar 2020 10:43:24 +1100 Subject: [PATCH 104/220] Fixed formatting in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4c3156..996f2d0 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [shodan](misp_modules/modules/expansion/shodan.py) - a minimal [shodan](https://www.shodan.io/) expansion module. * [Sigma queries](misp_modules/modules/expansion/sigma_queries.py) - Experimental expansion module querying a sigma rule to convert it into all the available SIEM signatures. * [Sigma syntax validator](misp_modules/modules/expansion/sigma_syntax_validator.py) - Sigma syntax validator. -* [SOPHOSLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SOPHOSLabs Intelix is an API for Threat Intelligence and Analysis (Free tier)[SOPHOSLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS) +* [SophosLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SophosLabs Intelix is an API for Threat Intelligence and Analysis (free tier availible). [SophosLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS) * [sourcecache](misp_modules/modules/expansion/sourcecache.py) - a module to cache a specific link from a MISP instance. * [STIX2 pattern syntax validator](misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax. * [ThreatCrowd](misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/). From 0a8a829ac10650b5eb41e0e4d0e9f8b6606909b6 Mon Sep 17 00:00:00 2001 From: bennyv Date: Wed, 4 Mar 2020 11:30:44 +1100 Subject: [PATCH 105/220] Fixed handler error handling for missing config --- .../modules/expansion/sophoslabs_intelix.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 4cc65ca..e4dcab6 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -99,20 +99,19 @@ class SophosLabsApi(): def handler(q=False): if q is False: return False - request = json.loads(q) - if request['config']['client_id'] is None or request['config']['client_secret'] is None: + j = json.loads(q) + if not j.get('config') or not j['config'].get('client_id') or not j['config'].get('client_secret'): misperrors['error'] = "Missing client_id or client_secret value for SOPHOSLabs Intelix. \ - It's free to Sign Up here https://aws.amazon.com/marketplace/pp/B07SLZPMCS." + It's free to sign up here https://aws.amazon.com/marketplace/pp/B07SLZPMCS." return misperrors - else: - client = SophosLabsApi(request['config']['client_id'], request['config']['client_secret']) - if request['attribute']['type'] == "sha256": - client.hash_lookup(request['attribute']['value1']) - if request['attribute']['type'] in ['ip-dst', 'ip-src', 'ip']: - client.ip_lookup(request["attribute"]["value1"]) - if request['attribute']['type'] in ['uri', 'url', 'domain', 'hostname']: - client.url_lookup(request["attribute"]["value1"]) - return client.get_result() + client = SophosLabsApi(j['config']['client_id'], j['config']['client_secret']) + if j['attribute']['type'] == "sha256": + client.hash_lookup(j['attribute']['value1']) + if j['attribute']['type'] in ['ip-dst', 'ip-src', 'ip']: + client.ip_lookup(j["attribute"]["value1"]) + if j['attribute']['type'] in ['uri', 'url', 'domain', 'hostname']: + client.url_lookup(j["attribute"]["value1"]) + return client.get_result() def introspection(): From 6c00f02e42959c635d3b5a4b3bed5da67a845ce2 Mon Sep 17 00:00:00 2001 From: bennyv Date: Wed, 4 Mar 2020 11:54:55 +1100 Subject: [PATCH 106/220] Removed Unused Import --- misp_modules/modules/expansion/sophoslabs_intelix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index e4dcab6..57a1af0 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -1,4 +1,4 @@ -from pymisp import MISPAttribute, MISPEvent, MISPObject +from pymisp import MISPEvent, MISPObject import json import requests import base64 From 0b4d6738de501526d99fb7e9eebfc08dfd4722cf Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 10 Mar 2020 11:15:16 +0100 Subject: [PATCH 107/220] fix: Making pep8 happy --- misp_modules/modules/expansion/sophoslabs_intelix.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 57a1af0..017683a 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -18,16 +18,17 @@ misp_types_in = ['sha256', 'ip', 'ip-src', 'ip-dst', 'uri', 'url', 'domain', 'ho mispattributes = {'input': misp_types_in, 'format': 'misp_standard'} + class SophosLabsApi(): def __init__(self, client_id, client_secret): self.misp_event = MISPEvent() self.client_id = client_id - self.client_secret= client_secret + self.client_secret = client_secret self.authToken = f"{self.client_id}:{self.client_secret}" self.baseurl = 'de.api.labs.sophos.com' d = {'grant_type': 'client_credentials'} - h = {'Authorization': f"Basic {base64.b64encode(self.authToken.encode('UTF-8')).decode('ascii')}",\ - 'Content-Type': 'application/x-www-form-urlencoded'} + h = {'Authorization': f"Basic {base64.b64encode(self.authToken.encode('UTF-8')).decode('ascii')}", + 'Content-Type': 'application/x-www-form-urlencoded'} r = requests.post('https://api.labs.sophos.com/oauth2/token', headers=h, data=d) if r.status_code == 200: j = json.loads(r.text) @@ -83,12 +84,12 @@ class SophosLabsApi(): sophos_object.add_attribute('URL Categorisation', type='text', value=j['productivityCategory']) else: sophos_object.add_attribute('URL Categorisation', type='text', value='No category assocaited with IoC') - + if 'riskLevel' in j: sophos_object.add_attribute('URL Risk Level', type='text', value=j['riskLevel']) else: sophos_object.add_attribute('URL Risk Level', type='text', value='No risk level associated with IoC') - + if 'securityCategory' in j: sophos_object.add_attribute('URL Security Category', type='text', value=j['securityCategory']) else: @@ -121,4 +122,3 @@ def introspection(): def version(): moduleinfo['config'] = moduleconfig return moduleinfo - From e023f0b4700122d18f94a7e5663b2a712babfcb5 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Tue, 10 Mar 2020 18:25:30 +0100 Subject: [PATCH 108/220] Cytomic Orion MISP Module An expansion module to enrich attributes in MISP and share indicators of compromise with Cytomic Orion --- README.md | 1 + misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/cytomic_orion.py | 183 ++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100755 misp_modules/modules/expansion/cytomic_orion.py diff --git a/README.md b/README.md index 996f2d0..fe37cd5 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [CVE](misp_modules/modules/expansion/cve.py) - a hover module to give more information about a vulnerability (CVE). * [CVE advanced](misp_modules/modules/expansion/cve_advanced.py) - An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE). * [Cuckoo submit](misp_modules/modules/expansion/cuckoo_submit.py) - A hover module to submit malware sample, url, attachment, domain to Cuckoo Sandbox. +* [Cytomic Orion](misp_modules/modules/expansion/cytomic_orion.py) - An expansion module to enrich attributes in MISP and share indicators of compromise with Cytomic Orion. * [DBL Spamhaus](misp_modules/modules/expansion/dbl_spamhaus.py) - a hover module to check Spamhaus DBL for a domain name. * [DNS](misp_modules/modules/expansion/dns.py) - a simple module to resolve MISP attributes like hostname and domain to expand IP addresses attributes. * [docx-enrich](misp_modules/modules/expansion/docx_enrich.py) - an enrichment module to get text out of Word document into MISP (using free-text parser). diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 8eb0a92..53c8ad8 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -16,4 +16,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', - 'lastline_query', 'lastline_submit', 'sophoslabs_intelix'] + 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion.py'] diff --git a/misp_modules/modules/expansion/cytomic_orion.py b/misp_modules/modules/expansion/cytomic_orion.py new file mode 100755 index 0000000..897840b --- /dev/null +++ b/misp_modules/modules/expansion/cytomic_orion.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 + +''' +Cytomic Orion MISP Module +An expansion module to enrich attributes in MISP and share indicators of compromise with Cytomic Orion + + +''' + +from pymisp import MISPAttribute, MISPEvent, MISPObject, MISPTag +import json +import requests +import re + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['md5'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.3', 'author': 'Koen Van Impe', + 'description': 'an expansion module to enrich attributes in MISP and share indicators of compromise with Cytomic Orion', + 'module-type': ['expansion']} +moduleconfig = ['api_url', 'token_url', 'clientid', 'clientsecret', 'clientsecret', 'username', 'password', 'upload_timeframe', 'upload_tag', 'delete_tag', 'upload_ttlDays', 'upload_threat_level_id', 'limit_upload_events', 'limit_upload_attributes'] +# There are more config settings in this module than used by the enrichment +# There is also a PyMISP module which reuses the module config, and requires additional configuration, for example used for pushing indicators to the API + + +class CytomicParser(): + def __init__(self, attribute, config_object): + self.misp_event = MISPEvent() + self.attribute = MISPAttribute() + self.attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.attribute) + + self.config_object = config_object + + if self.config_object: + self.token = self.get_token() + else: + return {'error': 'Missing configuration'} + + def get_token(self): + try: + scope = self.config_object['scope'] + grant_type = self.config_object['grant_type'] + username = self.config_object['username'] + password = self.config_object['password'] + token_url = self.config_object['token_url'] + clientid = self.config_object['clientid'] + clientsecret = self.config_object['clientsecret'] + + if scope and grant_type and username and password: + data = {'scope': scope, 'grant_type': grant_type, 'username': username, 'password': password} + + if token_url and clientid and clientsecret: + access_token_response = requests.post(token_url, data=data, verify=False, allow_redirects=False, auth=(clientid, clientsecret)) + tokens = json.loads(access_token_response.text) + if 'access_token' in tokens: + return tokens['access_token'] + else: + self.result = {'error': 'No token received.'} + return + else: + self.result = {'error': 'No token_url, clientid or clientsecret supplied.'} + return + else: + self.result = {'error': 'No scope, grant_type, username or password supplied.'} + return + except Exception: + self.result = {'error': 'Unable to connect to token_url.'} + return + + def get_results(self): + if hasattr(self, 'result'): + return self.result + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + def parse(self, searchkey): + + if self.token: + + endpoint_fileinformation = self.config_object['endpoint_fileinformation'] + endpoint_machines = self.config_object['endpoint_machines'] + endpoint_machines_client = self.config_object['endpoint_machines_client'] + query_machines = self.config_object['query_machines'] + query_machine_info = self.config_object['query_machine_info'] + + # Update endpoint URLs + query_endpoint_fileinformation = endpoint_fileinformation.format(md5=searchkey) + query_endpoint_machines = endpoint_machines.format(md5=searchkey) + + # API calls + api_call_headers = {'Authorization': 'Bearer ' + self.token} + result_query_endpoint_fileinformation = requests.get(query_endpoint_fileinformation, headers=api_call_headers, verify=False) + json_result_query_endpoint_fileinformation = json.loads(result_query_endpoint_fileinformation.text) + + if json_result_query_endpoint_fileinformation: + + cytomic_object = MISPObject('cytomic-orion-file') + + cytomic_object.add_attribute('fileName', type='text', value=json_result_query_endpoint_fileinformation['fileName']) + cytomic_object.add_attribute('fileSize', type='text', value=json_result_query_endpoint_fileinformation['fileSize']) + cytomic_object.add_attribute('last-seen', type='datetime', value=json_result_query_endpoint_fileinformation['lastSeen']) + cytomic_object.add_attribute('first-seen', type='datetime', value=json_result_query_endpoint_fileinformation['firstSeen']) + cytomic_object.add_attribute('classification', type='text', value=json_result_query_endpoint_fileinformation['classification']) + cytomic_object.add_attribute('classificationName', type='text', value=json_result_query_endpoint_fileinformation['classificationName']) + self.misp_event.add_object(**cytomic_object) + + result_query_endpoint_machines = requests.get(query_endpoint_machines, headers=api_call_headers, verify=False) + json_result_query_endpoint_machines = json.loads(result_query_endpoint_machines.text) + + if json_result_query_endpoint_machines and len(json_result_query_endpoint_machines) > 0: + for machine in json_result_query_endpoint_machines: + + if machine['muid'] and query_machine_info: + query_endpoint_machines_client = endpoint_machines_client.format(muid=machine['muid']) + result_endpoint_machines_client = requests.get(query_endpoint_machines_client, headers=api_call_headers, verify=False) + json_result_endpoint_machines_client = json.loads(result_endpoint_machines_client.text) + + if json_result_endpoint_machines_client: + + cytomic_machine_object = MISPObject('cytomic-orion-machine') + + clienttag = [{'name': json_result_endpoint_machines_client['clientName']}] + + cytomic_machine_object.add_attribute('machineName', type='target-machine', value=json_result_endpoint_machines_client['machineName'], Tag=clienttag) + cytomic_machine_object.add_attribute('machineMuid', type='text', value=machine['muid']) + cytomic_machine_object.add_attribute('clientName', type='target-org', value=json_result_endpoint_machines_client['clientName'], Tag=clienttag) + cytomic_machine_object.add_attribute('clientId', type='text', value=machine['clientId']) + cytomic_machine_object.add_attribute('machinePath', type='text', value=machine['lastPath']) + cytomic_machine_object.add_attribute('first-seen', type='datetime', value=machine['firstSeen']) + cytomic_machine_object.add_attribute('last-seen', type='datetime', value=machine['lastSeen']) + cytomic_machine_object.add_attribute('creationDate', type='datetime', value=json_result_endpoint_machines_client['creationDate']) + cytomic_machine_object.add_attribute('clientCreationDateUTC', type='datetime', value=json_result_endpoint_machines_client['clientCreationDateUTC']) + cytomic_machine_object.add_attribute('lastSeenUtc', type='datetime', value=json_result_endpoint_machines_client['lastSeenUtc']) + self.misp_event.add_object(**cytomic_machine_object) + else: + self.result = {'error': 'No (valid) token.'} + return + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + + if not request.get('attribute'): + return {'error': 'Unsupported input.'} + + attribute = request['attribute'] + if not any(input_type == attribute['type'] for input_type in mispattributes['input']): + return {'error': 'Unsupported attributes type'} + + if not request.get('config'): + return {'error': 'Missing configuration'} + + config_object = { + 'clientid': request["config"].get("clientid"), + 'clientsecret': request["config"].get("clientsecret"), + 'scope': 'orion.api', + 'password': request["config"].get("password"), + 'username': request["config"].get("username"), + 'grant_type': 'password', + 'token_url': request["config"].get("token_url"), + 'endpoint_fileinformation': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/info'), + 'endpoint_machines': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/muids'), + 'endpoint_machines_client': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/muid/{muid}/info'), + 'query_machines': True, + 'query_machine_info': True + } + + cytomic_parser = CytomicParser(attribute, config_object) + cytomic_parser.parse(attribute['value']) + + return cytomic_parser.get_results() + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From c86f4a418053718eea07e1baa64d7d5db096a3d4 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Tue, 10 Mar 2020 18:48:25 +0100 Subject: [PATCH 109/220] Make Travis (a little bit) happy --- misp_modules/modules/expansion/cytomic_orion.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misp_modules/modules/expansion/cytomic_orion.py b/misp_modules/modules/expansion/cytomic_orion.py index 897840b..c7830e8 100755 --- a/misp_modules/modules/expansion/cytomic_orion.py +++ b/misp_modules/modules/expansion/cytomic_orion.py @@ -7,10 +7,10 @@ An expansion module to enrich attributes in MISP and share indicators of comprom ''' -from pymisp import MISPAttribute, MISPEvent, MISPObject, MISPTag +from pymisp import MISPAttribute, MISPEvent, MISPObject import json import requests -import re +import sys misperrors = {'error': 'Error'} mispattributes = {'input': ['md5'], 'format': 'misp_standard'} @@ -34,7 +34,7 @@ class CytomicParser(): if self.config_object: self.token = self.get_token() else: - return {'error': 'Missing configuration'} + sys.exit('Missing configuration') def get_token(self): try: @@ -108,10 +108,10 @@ class CytomicParser(): result_query_endpoint_machines = requests.get(query_endpoint_machines, headers=api_call_headers, verify=False) json_result_query_endpoint_machines = json.loads(result_query_endpoint_machines.text) - if json_result_query_endpoint_machines and len(json_result_query_endpoint_machines) > 0: + if query_machines and json_result_query_endpoint_machines and len(json_result_query_endpoint_machines) > 0: for machine in json_result_query_endpoint_machines: - if machine['muid'] and query_machine_info: + if query_machine_info and machine['muid']: query_endpoint_machines_client = endpoint_machines_client.format(muid=machine['muid']) result_endpoint_machines_client = requests.get(query_endpoint_machines_client, headers=api_call_headers, verify=False) json_result_endpoint_machines_client = json.loads(result_endpoint_machines_client.text) From 2713d3c6555f3e7f36a8aa0364a74a71ec1bd85a Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Tue, 10 Mar 2020 19:50:00 +0100 Subject: [PATCH 110/220] Update __init__ --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 53c8ad8..2a99050 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -16,4 +16,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', - 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion.py'] + 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion'] From d2f0d8027bd3f380198a22bbfb7ca300fd39b1fb Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Wed, 11 Mar 2020 11:56:12 +0100 Subject: [PATCH 111/220] Documentation for Cytomic Orion --- doc/expansion/cytomic_orion.py | 9 +++++++++ doc/logos/cytomic_orion.png | Bin 0 -> 898 bytes 2 files changed, 9 insertions(+) create mode 100644 doc/expansion/cytomic_orion.py create mode 100644 doc/logos/cytomic_orion.png diff --git a/doc/expansion/cytomic_orion.py b/doc/expansion/cytomic_orion.py new file mode 100644 index 0000000..6f87657 --- /dev/null +++ b/doc/expansion/cytomic_orion.py @@ -0,0 +1,9 @@ +{ + "description": "An expansion module to enrich attributes in MISP by quering the Cytomic Orion API", + "logo": "logos/cytomic_orion.png", + "requirements": ["Access (license) to Cytomic Orion"], + "input": "MD5, hash of the sample / malware to search for.", + "output": "MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines.", + "references": ["https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/", "https://www.cytomicmodel.com/solutions/"], + "features": "This module takes an MD5 hash and searches for occurrences of this hash in the Cytomic Orion database. Returns observed files and machines." +} diff --git a/doc/logos/cytomic_orion.png b/doc/logos/cytomic_orion.png new file mode 100644 index 0000000000000000000000000000000000000000..45704e9278088bb5305b3b0b20fcb4158b8fe2ee GIT binary patch literal 898 zcmV-|1AY97P)fVtcD? zYC!oEge*lRQ+<$8WgRLQmX~&DAQ85>=1)@bO%R&r)SEd4S*k8mCidD-B(Y&t=*w~O z;XcTeR4$)D$+J2)=BFSvN6ND|M32|YY?V2PiyQjV5Nq}ZH~WGw9F^OSNMlH3F?>0Q z@M}P>toG{WoDSjU4Yc_bWRt1fD#q4Gya7Tas1l?LVoPKTTb+PjmogaJD4YdTYTlZb zTOf0LGTGWRr^DO;Td?kexJ+sgQ75me@*y$5@)V?-^gAGHOjLcSJqB?jZSHq& z5la3U2oGU;3Tv==+&u(wsWhjeo5JiIWIy!~8udwIkjq+iE&2|K@VjBS|D1z#7yI@0 zYjnUR`DKuB6AdGC4#L_|y;Lfc_s_WDs(-pHR6l<%iXj?D}4NAl(nE7D=@~eG1aa z4GaqJ=UtHQr*8GJt9r@%35d(3xB*iBD53dfSZL718|U7yLCk6B&+H43fx!tRE3aao z464#g`}l7Be7mDA2n;&MpK{{z2c6ip^SU>^{zP366bgkxp-?Ck3WY+UP$(1%h5AeS Y2jOxQJ{Y2W1^@s607*qoM6N<$g6^}Z-~a#s literal 0 HcmV?d00001 From fe34023866da011b175709a862d6efec579b56b3 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Thu, 12 Mar 2020 11:02:43 +0100 Subject: [PATCH 112/220] csvimport: Return error if input is not valid UTF-8 --- misp_modules/modules/import_mod/csvimport.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index 8bfbbe9..38e5f96 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -256,7 +256,11 @@ def handler(q=False): return False request = json.loads(q) if request.get('data'): - data = base64.b64decode(request['data']).decode('utf-8') + try: + data = base64.b64decode(request['data']).decode('utf-8') + except UnicodeDecodeError: + misperrors['error'] = "Input is not valid UTF-8" + return misperrors else: misperrors['error'] = "Unsupported attributes type" return misperrors From 422f654988c94a32d78a4e0aa81d7785497ea718 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Mar 2020 10:24:06 +0100 Subject: [PATCH 113/220] fix: Making pep8 happy with indentation --- .../modules/expansion/cytomic_orion.py | 26 +++++++++---------- misp_modules/modules/import_mod/csvimport.py | 6 ++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/misp_modules/modules/expansion/cytomic_orion.py b/misp_modules/modules/expansion/cytomic_orion.py index c7830e8..9723ed6 100755 --- a/misp_modules/modules/expansion/cytomic_orion.py +++ b/misp_modules/modules/expansion/cytomic_orion.py @@ -154,19 +154,19 @@ def handler(q=False): return {'error': 'Missing configuration'} config_object = { - 'clientid': request["config"].get("clientid"), - 'clientsecret': request["config"].get("clientsecret"), - 'scope': 'orion.api', - 'password': request["config"].get("password"), - 'username': request["config"].get("username"), - 'grant_type': 'password', - 'token_url': request["config"].get("token_url"), - 'endpoint_fileinformation': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/info'), - 'endpoint_machines': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/muids'), - 'endpoint_machines_client': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/muid/{muid}/info'), - 'query_machines': True, - 'query_machine_info': True - } + 'clientid': request["config"].get("clientid"), + 'clientsecret': request["config"].get("clientsecret"), + 'scope': 'orion.api', + 'password': request["config"].get("password"), + 'username': request["config"].get("username"), + 'grant_type': 'password', + 'token_url': request["config"].get("token_url"), + 'endpoint_fileinformation': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/info'), + 'endpoint_machines': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/muids'), + 'endpoint_machines_client': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/muid/{muid}/info'), + 'query_machines': True, + 'query_machine_info': True + } cytomic_parser = CytomicParser(attribute, config_object) cytomic_parser.parse(attribute['value']) diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index 38e5f96..e4dc2e5 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -257,10 +257,10 @@ def handler(q=False): request = json.loads(q) if request.get('data'): try: - data = base64.b64decode(request['data']).decode('utf-8') + data = base64.b64decode(request['data']).decode('utf-8') except UnicodeDecodeError: - misperrors['error'] = "Input is not valid UTF-8" - return misperrors + misperrors['error'] = "Input is not valid UTF-8" + return misperrors else: misperrors['error'] = "Unsupported attributes type" return misperrors From 824c0031b3aa867284a00ecf60ad992e86d6b5ef Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Mar 2020 17:57:55 +0100 Subject: [PATCH 114/220] fix: Catching errors in the reponse of the query to URLhaus --- misp_modules/modules/expansion/urlhaus.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/urlhaus.py b/misp_modules/modules/expansion/urlhaus.py index 30b78ee..baaaaf6 100644 --- a/misp_modules/modules/expansion/urlhaus.py +++ b/misp_modules/modules/expansion/urlhaus.py @@ -35,6 +35,11 @@ class URLhaus(): results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} return {'results': results} + def parse_error(self, query_status): + if query_status == 'no_results': + return {'error': f'No results found on URLhaus for this {self.attribute.type} attribute'} + return {'error': f'Error encountered during the query of URLhaus: {query_status}'} + class HostQuery(URLhaus): def __init__(self, attribute): @@ -45,9 +50,12 @@ class HostQuery(URLhaus): def query_api(self): response = requests.post(self.url, data={'host': self.attribute.value}).json() + if response['query_status'] != 'ok': + return self.parse_error(response['query_status']) if 'urls' in response and response['urls']: for url in response['urls']: self.misp_event.add_attribute(type='url', value=url['url']) + return self.get_result() class PayloadQuery(URLhaus): @@ -63,6 +71,8 @@ class PayloadQuery(URLhaus): if hasattr(self.attribute, 'object_id') and hasattr(self.attribute, 'event_id') and self.attribute.event_id != '0': file_object.id = self.attribute.object_id response = requests.post(self.url, data={'{}_hash'.format(hash_type): self.attribute.value}).json() + if response['query_status'] != 'ok': + return self.parse_error(response['query_status']) other_hash_type = 'md5' if hash_type == 'sha256' else 'sha256' for key, relation in zip(('{}_hash'.format(other_hash_type), 'file_size'), (other_hash_type, 'size-in-bytes')): if response[key]: @@ -81,6 +91,7 @@ class PayloadQuery(URLhaus): file_object.add_attribute(_filename_, **{'type': _filename_, 'value': url[_filename_]}) if any((file_object.attributes, file_object.references)): self.misp_event.add_object(**file_object) + return self.get_result() class UrlQuery(URLhaus): @@ -100,6 +111,8 @@ class UrlQuery(URLhaus): def query_api(self): response = requests.post(self.url, data={'url': self.attribute.value}).json() + if response['query_status'] != 'ok': + return self.parse_error(response['query_status']) if 'payloads' in response and response['payloads']: for payload in response['payloads']: file_object = self._create_file_object(payload) @@ -109,6 +122,7 @@ class UrlQuery(URLhaus): self.misp_event.add_object(**vt_object) if any((file_object.attributes, file_object.references)): self.misp_event.add_object(**file_object) + return self.get_result() _misp_type_mapping = {'url': UrlQuery, 'md5': PayloadQuery, 'sha256': PayloadQuery, @@ -122,8 +136,7 @@ def handler(q=False): request = json.loads(q) attribute = request['attribute'] urlhaus_parser = _misp_type_mapping[attribute['type']](attribute) - urlhaus_parser.query_api() - return urlhaus_parser.get_result() + return urlhaus_parser.query_api() def introspection(): From 0671f93724e75d584a1ac9ca94f6e272663e5cd3 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Mar 2020 18:05:57 +0100 Subject: [PATCH 115/220] new: Expansion module to query MALWAREbazaar API with some hash attribute --- misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/malwarebazaar.py | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/malwarebazaar.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 2a99050..a8e35db 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -15,5 +15,5 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'qrcode', 'ocr_enrich', 'pdf_enrich', 'docx_enrich', 'xlsx_enrich', 'pptx_enrich', 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', - 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', + 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion'] diff --git a/misp_modules/modules/expansion/malwarebazaar.py b/misp_modules/modules/expansion/malwarebazaar.py new file mode 100644 index 0000000..4930fd6 --- /dev/null +++ b/misp_modules/modules/expansion/malwarebazaar.py @@ -0,0 +1,53 @@ +import json +import requests +from pymisp import MISPEvent, MISPObject + +mispattributes = {'input': ['md5', 'sha1', 'sha256'], + 'format': 'misp_standard'} +moduleinfo = {'version': '0.1', 'author': 'Christian Studer', + 'description': 'Query Malware Bazaar to get additional information about the input hash.', + 'module-type': ['expansion', 'hover']} +moduleconfig = [] + + + +def parse_response(response): + mapping = {'file_name': {'type': 'filename', 'object_relation': 'filename'}, + 'file_size': {'type': 'size-in-bytes', 'object_relation': 'size-in-bytes'}, + 'file_type_mime': {'type': 'mime-type', 'object_relation': 'mimetype'}, + 'md5_hash': {'type': 'md5', 'object_relation': 'md5'}, + 'sha1_hash': {'type': 'sha1', 'object_relation': 'sha1'}, + 'sha256_hash': {'type': 'sha256', 'object_relation': 'sha256'}, + 'ssdeep': {'type': 'ssdeep', 'object_relation': 'ssdeep'}} + misp_event = MISPEvent() + for data in response: + misp_object = MISPObject('file') + for feature, attribute in mapping.items(): + if feature in data: + misp_attribute = {'value': data[feature]} + misp_attribute.update(attribute) + misp_object.add_attribute(**misp_attribute) + misp_event.add_object(**misp_object) + return {'results': {'Object': [json.loads(misp_object.to_json()) for misp_object in misp_event.objects]}} + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + attribute = request['attribute'] + url = 'https://mb-api.abuse.ch/api/v1/' + response = requests.post(url, data={'query': 'get_info', 'hash': attribute['value']}).json() + query_status = response['query_status'] + if query_status == 'ok': + return parse_response(response['data']) + return {'error': 'Hash not found on MALWAREbazzar' if query_status == 'hash_not_found' else f'Problem encountered during the query: {query_status}'} + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 8805bd86492aac6531fe64610e7d7706c2b632c1 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Mar 2020 18:42:26 +0100 Subject: [PATCH 116/220] add: Added documentation for the latest new modules --- README.md | 1 + doc/README.md | 34 ++++++++++++++++++++++++++++++++ doc/expansion/malwarebazaar.json | 8 ++++++++ 3 files changed, 43 insertions(+) create mode 100644 doc/expansion/malwarebazaar.json diff --git a/README.md b/README.md index fe37cd5..165f3c5 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [Lastline query](misp_modules/modules/expansion/lastline_query.py) - Query Lastline with the link to an analysis and parse the report. * [macaddress.io](misp_modules/modules/expansion/macaddress_io.py) - a hover module to retrieve vendor details and other information regarding a given MAC address or an OUI from [MAC address Vendor Lookup](https://macaddress.io). See [integration tutorial here](https://macaddress.io/integrations/MISP-module). * [macvendors](misp_modules/modules/expansion/macvendors.py) - a hover module to retrieve mac vendor information. +* [MALWAREbazaar](misp_modules/modules/expansion/malwarebazaar.py) - an expansion module to query MALWAREbazaar with some payload. * [ocr-enrich](misp_modules/modules/expansion/ocr_enrich.py) - an enrichment module to get OCRized data from images into MISP. * [ods-enrich](misp_modules/modules/expansion/ods_enrich.py) - an enrichment module to get text out of OpenOffice spreadsheet document into MISP (using free-text parser). * [odt-enrich](misp_modules/modules/expansion/odt_enrich.py) - an enrichment module to get text out of OpenOffice document into MISP (using free-text parser). diff --git a/doc/README.md b/doc/README.md index 7e6bee3..9f221bd 100644 --- a/doc/README.md +++ b/doc/README.md @@ -295,6 +295,24 @@ An expansion hover module to expand information about CVE id. ----- +#### [cytomic_orion.py](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cytomic_orion.py.py) + + + +An expansion module to enrich attributes in MISP by quering the Cytomic Orion API +- **features**: +>This module takes an MD5 hash and searches for occurrences of this hash in the Cytomic Orion database. Returns observed files and machines. +- **input**: +>MD5, hash of the sample / malware to search for. +- **output**: +>MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines. +- **references**: +>https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/, https://www.cytomicmodel.com/solutions/ +- **requirements**: +>Access (license) to Cytomic Orion + +----- + #### [dbl_spamhaus](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/dbl_spamhaus.py) @@ -681,6 +699,22 @@ Module to access Macvendors API. ----- +#### [malwarebazaar](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/malwarebazaar.py) + +Query the MALWAREbazaar API to get additional information about the input hash attribute. +- **features**: +>The module takes a hash attribute as input and queries MALWAREbazaar's API to fetch additional data about it. The result, if the payload is known on the databases, is at least one file object describing the file the input hash is related to. +> +>The module is using the new format of modules able to return object since the result is one or multiple MISP object(s). +- **input**: +>A hash attribute (md5, sha1 or sha256). +- **output**: +>File object(s) related to the input attribute found on MALWAREbazaar databases. +- **references**: +>https://bazaar.abuse.ch/ + +----- + #### [ocr-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ocr-enrich.py) Module to process some optical character recognition on pictures. diff --git a/doc/expansion/malwarebazaar.json b/doc/expansion/malwarebazaar.json new file mode 100644 index 0000000..2db6ad5 --- /dev/null +++ b/doc/expansion/malwarebazaar.json @@ -0,0 +1,8 @@ +{ + "description": "Query the MALWAREbazaar API to get additional information about the input hash attribute.", + "requirements": [], + "input": "A hash attribute (md5, sha1 or sha256).", + "output": "File object(s) related to the input attribute found on MALWAREbazaar databases.", + "references": ["https://bazaar.abuse.ch/"], + "features": "The module takes a hash attribute as input and queries MALWAREbazaar's API to fetch additional data about it. The result, if the payload is known on the databases, is at least one file object describing the file the input hash is related to.\n\nThe module is using the new format of modules able to return object since the result is one or multiple MISP object(s)." +} From 48b381d704ac9c1a1efcaccd9d8758f715d771dd Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Mar 2020 18:58:11 +0100 Subject: [PATCH 117/220] fix: Making pep8 happy --- misp_modules/modules/expansion/malwarebazaar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misp_modules/modules/expansion/malwarebazaar.py b/misp_modules/modules/expansion/malwarebazaar.py index 4930fd6..4574b75 100644 --- a/misp_modules/modules/expansion/malwarebazaar.py +++ b/misp_modules/modules/expansion/malwarebazaar.py @@ -10,7 +10,6 @@ moduleinfo = {'version': '0.1', 'author': 'Christian Studer', moduleconfig = [] - def parse_response(response): mapping = {'file_name': {'type': 'filename', 'object_relation': 'filename'}, 'file_size': {'type': 'size-in-bytes', 'object_relation': 'size-in-bytes'}, From 9c0ebfb3b7dc29aadd7f99778d3057e26450d4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Sat, 28 Mar 2020 18:41:25 +0100 Subject: [PATCH 118/220] chg: Bump dependencies Should fix https://github.com/MISP/MISP/issues/5739 --- Pipfile.lock | 836 +++++++++++++++++++++++++-------------------------- REQUIREMENTS | 100 +++--- 2 files changed, 468 insertions(+), 468 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index b977ce7..ac5749a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -45,10 +45,10 @@ }, "antlr4-python3-runtime": { "hashes": [ - "sha256:168cdcec8fb9152e84a87ca6fd261b3d54c8f6358f42ab3b813b14a7193bb50b" + "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33" ], "markers": "python_version >= '3'", - "version": "==4.7.2" + "version": "==4.8" }, "apiosintds": { "hashes": [ @@ -119,41 +119,36 @@ }, "cffi": { "hashes": [ - "sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42", - "sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04", - "sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5", - "sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54", - "sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba", - "sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57", - "sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396", - "sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12", - "sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97", - "sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43", - "sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db", - "sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3", - "sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b", - "sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579", - "sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346", - "sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159", - "sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652", - "sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e", - "sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a", - "sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506", - "sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f", - "sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d", - "sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c", - "sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20", - "sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858", - "sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc", - "sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a", - "sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3", - "sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e", - "sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410", - "sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25", - "sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b", - "sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d" + "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff", + "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b", + "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac", + "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0", + "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384", + "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26", + "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6", + "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b", + "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e", + "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd", + "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2", + "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66", + "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc", + "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8", + "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55", + "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4", + "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5", + "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d", + "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78", + "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa", + "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793", + "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f", + "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a", + "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f", + "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30", + "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f", + "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3", + "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c" ], - "version": "==1.13.2" + "version": "==1.14.0" }, "chardet": { "hashes": [ @@ -164,10 +159,10 @@ }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", + "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" ], - "version": "==7.0" + "version": "==7.1.1" }, "click-plugins": { "hashes": [ @@ -211,10 +206,10 @@ }, "decorator": { "hashes": [ - "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce", - "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d" + "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", + "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" ], - "version": "==4.4.1" + "version": "==4.4.2" }, "deprecated": { "hashes": [ @@ -282,17 +277,17 @@ }, "httplib2": { "hashes": [ - "sha256:1d1f4ad7a6e55d325830ab274190f98894e069850a871fac19921caf4363259d", - "sha256:a5f914f18f99cb9541660454a159e3b3c63241fc3ab60005bb88d97cc7a4fb58" + "sha256:79751cc040229ec896aa01dced54de0cd0bf042f928e84d5761294422dde4454", + "sha256:de96d0a49f46d0ee7e0aae80141d37b8fcd6a68fb05d02e0b82c128592dd8261" ], - "version": "==0.15.0" + "version": "==0.17.0" }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" ], - "version": "==2.8" + "version": "==2.9" }, "idna-ssl": { "hashes": [ @@ -304,11 +299,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", - "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" + "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", + "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" ], "markers": "python_version < '3.8'", - "version": "==1.3.0" + "version": "==1.6.0" }, "isodate": { "hashes": [ @@ -352,35 +347,36 @@ }, "lxml": { "hashes": [ - "sha256:00ac0d64949fef6b3693813fe636a2d56d97a5a49b5bbb86e4cc4cc50ebc9ea2", - "sha256:0571e607558665ed42e450d7bf0e2941d542c18e117b1ebbf0ba72f287ad841c", - "sha256:0e3f04a7615fdac0be5e18b2406529521d6dbdb0167d2a690ee328bef7807487", - "sha256:13cf89be53348d1c17b453867da68704802966c433b2bb4fa1f970daadd2ef70", - "sha256:217262fcf6a4c2e1c7cb1efa08bd9ebc432502abc6c255c4abab611e8be0d14d", - "sha256:223e544828f1955daaf4cefbb4853bc416b2ec3fd56d4f4204a8b17007c21250", - "sha256:277cb61fede2f95b9c61912fefb3d43fbd5f18bf18a14fae4911b67984486f5d", - "sha256:3213f753e8ae86c396e0e066866e64c6b04618e85c723b32ecb0909885211f74", - "sha256:4690984a4dee1033da0af6df0b7a6bde83f74e1c0c870623797cec77964de34d", - "sha256:4fcc472ef87f45c429d3b923b925704aa581f875d65bac80f8ab0c3296a63f78", - "sha256:61409bd745a265a742f2693e4600e4dbd45cc1daebe1d5fad6fcb22912d44145", - "sha256:678f1963f755c5d9f5f6968dded7b245dd1ece8cf53c1aa9d80e6734a8c7f41d", - "sha256:6c6d03549d4e2734133badb9ab1c05d9f0ef4bcd31d83e5d2b4747c85cfa21da", - "sha256:6e74d5f4d6ecd6942375c52ffcd35f4318a61a02328f6f1bd79fcb4ffedf969e", - "sha256:7b4fc7b1ecc987ca7aaf3f4f0e71bbfbd81aaabf87002558f5bc95da3a865bcd", - "sha256:7ed386a40e172ddf44c061ad74881d8622f791d9af0b6f5be20023029129bc85", - "sha256:8f54f0924d12c47a382c600c880770b5ebfc96c9fd94cf6f6bdc21caf6163ea7", - "sha256:ad9b81351fdc236bda538efa6879315448411a81186c836d4b80d6ca8217cdb9", - "sha256:bbd00e21ea17f7bcc58dccd13869d68441b32899e89cf6cfa90d624a9198ce85", - "sha256:c3c289762cc09735e2a8f8a49571d0e8b4f57ea831ea11558247b5bdea0ac4db", - "sha256:cf4650942de5e5685ad308e22bcafbccfe37c54aa7c0e30cd620c2ee5c93d336", - "sha256:cfcbc33c9c59c93776aa41ab02e55c288a042211708b72fdb518221cc803abc8", - "sha256:e301055deadfedbd80cf94f2f65ff23126b232b0d1fea28f332ce58137bcdb18", - "sha256:ebbfe24df7f7b5c6c7620702496b6419f6a9aa2fd7f005eb731cc80d7b4692b9", - "sha256:eff69ddbf3ad86375c344339371168640951c302450c5d3e9936e98d6459db06", - "sha256:f6ed60a62c5f1c44e789d2cf14009423cb1646b44a43e40a9cf6a21f077678a1" + "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd", + "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c", + "sha256:1f2c4ec372bf1c4a2c7e4bb20845e8bcf8050365189d86806bad1e3ae473d081", + "sha256:4235bc124fdcf611d02047d7034164897ade13046bda967768836629bc62784f", + "sha256:5828c7f3e615f3975d48f40d4fe66e8a7b25f16b5e5705ffe1d22e43fb1f6261", + "sha256:585c0869f75577ac7a8ff38d08f7aac9033da2c41c11352ebf86a04652758b7a", + "sha256:5d467ce9c5d35b3bcc7172c06320dddb275fea6ac2037f72f0a4d7472035cea9", + "sha256:63dbc21efd7e822c11d5ddbedbbb08cd11a41e0032e382a0fd59b0b08e405a3a", + "sha256:7bc1b221e7867f2e7ff1933165c0cec7153dce93d0cdba6554b42a8beb687bdb", + "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60", + "sha256:8a0ebda56ebca1a83eb2d1ac266649b80af8dd4b4a3502b2c1e09ac2f88fe128", + "sha256:90ed0e36455a81b25b7034038e40880189169c308a3df360861ad74da7b68c1a", + "sha256:95e67224815ef86924fbc2b71a9dbd1f7262384bca4bc4793645794ac4200717", + "sha256:afdb34b715daf814d1abea0317b6d672476b498472f1e5aacbadc34ebbc26e89", + "sha256:b4b2c63cc7963aedd08a5f5a454c9f67251b1ac9e22fd9d72836206c42dc2a72", + "sha256:d068f55bda3c2c3fcaec24bd083d9e2eede32c583faf084d6e4b9daaea77dde8", + "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3", + "sha256:deadf4df349d1dcd7b2853a2c8796593cc346600726eff680ed8ed11812382a7", + "sha256:df533af6f88080419c5a604d0d63b2c33b1c0c4409aba7d0cb6de305147ea8c8", + "sha256:e4aa948eb15018a657702fee0b9db47e908491c64d36b4a90f59a64741516e77", + "sha256:e5d842c73e4ef6ed8c1bd77806bf84a7cb535f9c0cf9b2c74d02ebda310070e1", + "sha256:ebec08091a22c2be870890913bdadd86fcd8e9f0f22bcb398abd3af914690c15", + "sha256:edc15fcfd77395e24543be48871c251f38132bb834d9fdfdad756adb6ea37679", + "sha256:f2b74784ed7e0bc2d02bd53e48ad6ba523c9b36c194260b7a5045071abbb1012", + "sha256:fa071559f14bd1e92077b1b5f6c22cf09756c6de7139370249eb372854ce51e6", + "sha256:fd52e796fee7171c4361d441796b64df1acfceb51f29e545e812f16d023c4bbc", + "sha256:fe976a0f1ef09b3638778024ab9fb8cde3118f203364212c198f71341c0715ca" ], "index": "pypi", - "version": "==4.4.2" + "version": "==4.5.0" }, "maclookup": { "hashes": [ @@ -400,34 +396,27 @@ "editable": true, "path": "." }, - "more-itertools": { - "hashes": [ - "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", - "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" - ], - "version": "==8.0.2" - }, "multidict": { "hashes": [ - "sha256:0f04bf4c15d8417401a10a650c349ccc0285943681bfd87d3690587d7714a9b4", - "sha256:15a61c0df2d32487e06f6084eabb48fd9e8b848315e397781a70caf9670c9d78", - "sha256:3c5e2dcbe6b04cbb4303e47a896757a77b676c5e5db5528be7ff92f97ba7ab95", - "sha256:5d2b32b890d9e933d3ced417924261802a857abdee9507b68c75014482145c03", - "sha256:5e5fb8bfebf87f2e210306bf9dd8de2f1af6782b8b78e814060ae9254ab1f297", - "sha256:63ba2be08d82ea2aa8b0f7942a74af4908664d26cb4ff60c58eadb1e33e7da00", - "sha256:73740fcdb38f0adcec85e97db7557615b50ec4e5a3e73e35878720bcee963382", - "sha256:78bed18e7f1eb21f3d10ff3acde900b4d630098648fe1d65bb4abfb3e22c4900", - "sha256:a02fade7b5476c4f88efe9593ff2f3286698d8c6d715ba4f426954f73f382026", - "sha256:aacbde3a8875352a640efa2d1b96e5244a29b0f8df79cbf1ec6470e86fd84697", - "sha256:be813fb9e5ce41a5a99a29cdb857144a1bd6670883586f995b940a4878dc5238", - "sha256:bfcad6da0b8839f01a819602aaa5c5a5b4c85ecbfae9b261a31df3d9262fb31e", - "sha256:c2bfc0db3166e68515bc4a2b9164f4f75ae9c793e9635f8651f2c9ffc65c8dad", - "sha256:c66d11870ae066499a3541963e6ce18512ca827c2aaeaa2f4e37501cee39ac5d", - "sha256:cc7f2202b753f880c2e4123f9aacfdb94560ba893e692d24af271dac41f8b8d9", - "sha256:d1f45e5bb126662ba66ee579831ce8837b1fd978115c9657e32eb3c75b92973d", - "sha256:ed5f3378c102257df9e2dc9ce6468dabf68bee9ec34969cfdc472631aba00316" + "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1", + "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35", + "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928", + "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969", + "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e", + "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78", + "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1", + "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136", + "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8", + "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2", + "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e", + "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4", + "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5", + "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd", + "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab", + "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20", + "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3" ], - "version": "==4.7.3" + "version": "==4.7.5" }, "np": { "hashes": [ @@ -438,29 +427,29 @@ }, "numpy": { "hashes": [ - "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6", - "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e", - "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc", - "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc", - "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a", - "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa", - "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3", - "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121", - "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971", - "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26", - "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd", - "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480", - "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec", - "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77", - "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57", - "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07", - "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572", - "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73", - "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca", - "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474", - "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5" + "sha256:1598a6de323508cfeed6b7cd6c4efb43324f4692e20d1f76e1feec7f59013448", + "sha256:1b0ece94018ae21163d1f651b527156e1f03943b986188dd81bc7e066eae9d1c", + "sha256:2e40be731ad618cb4974d5ba60d373cdf4f1b8dcbf1dcf4d9dff5e212baf69c5", + "sha256:4ba59db1fcc27ea31368af524dcf874d9277f21fd2e1f7f1e2e0c75ee61419ed", + "sha256:59ca9c6592da581a03d42cc4e270732552243dc45e87248aa8d636d53812f6a5", + "sha256:5e0feb76849ca3e83dd396254e47c7dba65b3fa9ed3df67c2556293ae3e16de3", + "sha256:6d205249a0293e62bbb3898c4c2e1ff8a22f98375a34775a259a0523111a8f6c", + "sha256:6fcc5a3990e269f86d388f165a089259893851437b904f422d301cdce4ff25c8", + "sha256:82847f2765835c8e5308f136bc34018d09b49037ec23ecc42b246424c767056b", + "sha256:87902e5c03355335fc5992a74ba0247a70d937f326d852fc613b7f53516c0963", + "sha256:9ab21d1cb156a620d3999dd92f7d1c86824c622873841d6b080ca5495fa10fef", + "sha256:a1baa1dc8ecd88fb2d2a651671a84b9938461e8a8eed13e2f0a812a94084d1fa", + "sha256:a244f7af80dacf21054386539699ce29bcc64796ed9850c99a34b41305630286", + "sha256:a35af656a7ba1d3decdd4fae5322b87277de8ac98b7d9da657d9e212ece76a61", + "sha256:b1fe1a6f3a6f355f6c29789b5927f8bd4f134a4bd9a781099a7c4f66af8850f5", + "sha256:b5ad0adb51b2dee7d0ee75a69e9871e2ddfb061c73ea8bc439376298141f77f5", + "sha256:ba3c7a2814ec8a176bb71f91478293d633c08582119e713a0c5351c0f77698da", + "sha256:cd77d58fb2acf57c1d1ee2835567cd70e6f1835e32090538f17f8a3a99e5e34b", + "sha256:cdb3a70285e8220875e4d2bc394e49b4988bdb1298ffa4e0bd81b2f613be397c", + "sha256:deb529c40c3f1e38d53d5ae6cd077c21f1d49e13afc7936f7f868455e16b64a0", + "sha256:e7894793e6e8540dbeac77c87b489e331947813511108ae097f1715c018b8f3d" ], - "version": "==1.18.1" + "version": "==1.18.2" }, "oauth2": { "hashes": [ @@ -477,60 +466,58 @@ }, "opencv-python": { "hashes": [ - "sha256:04bec0a6d3a00360a7fb769b755ff4489a4ac8291821b785151f63e6d8bb59ea", - "sha256:1a2d1801c038f055852bd2379186ca8b19b4ea24afb0b8410293bc802211579b", - "sha256:1c7d235faef511aca7669f1aa650897b6c058dfde6412ea3fc58feb0fce78814", - "sha256:22c2ee5f97f85903bfb28c056566b2ecaa1d2f804b880ab39ebf94528a402992", - "sha256:25127990671dc8bd27ae8b880d7a39f9aae863052a8fbebe8977c6ce8e5fc0c9", - "sha256:3cef82b6a1f748d2f4527f5932a86d54ebd10bd89f6cf59b003c36b1015055f7", - "sha256:499a0413e7110a934ab56e635252a4c86f8be64de59f94a62318a7b895dc809e", - "sha256:5f2cf5a0ab244a0a1dbe5ec426c277b55e06ac6a472ad61be77ef643a238cbd3", - "sha256:5fec35916a6b9ce935f2e2806084303fd4e3fbb0c973a8db8f54b5aca54613cb", - "sha256:6183c9c7fab4590e0651bc941cde780988c3ad9889bd62de19d581a6f59523ea", - "sha256:67a236db8db84d7fb0f6e127f360ce6669350ef324839132e22879ec90588dab", - "sha256:6c32d36f52a6e0c02d1ab0bb95223cb4dd5525a7e8292a747116126b3d34c578", - "sha256:73a467a78ffd902d2c0265ab6b2e2cdda423d61b3d08685e0c7d0b4572142ff1", - "sha256:76de8a247970d150b1672c6646cda91217d562682e713721fc9b9bf1434553c4", - "sha256:919d5c3ec1a62258ba8c68b869b1056186e2355c4474739b199c295547e66cc1", - "sha256:982d4e80c14356098cde57a6c7d18fe0928a1c3118675bac2252ef38f152e1ab", - "sha256:9d025e6bf2989bcbc7744c26d8bd90c2629a92d8de3ba2416f62ce2a94615dd9", - "sha256:bb59f98205cd81e29f45eed043cf0f98531486dc0b3f671c9e06fecf08f7ccef", - "sha256:c8119248457e909dcd7b598621ed1d139419d69377e8cb4e2b2c49c819de287d", - "sha256:ce7b1f25be04b04f2e678b2bf23a975137f77406dcee66a88a2daeb77cda3e76", - "sha256:d64428bf59ab4d27620b00a2ad6fea2b4d62016a17849c82a7517ec12db97d55", - "sha256:e2ffa3161b8662112f1880734e8b9549d0c9e818e59f652a9d1c5bf31e36586a", - "sha256:e6fc00ac42c800fad5fb3927cfb9bf4e60bb3302cb9805f45b826d5d2546119a", - "sha256:e793df2e12093b3a01006b5b27f321e306193c7a5c9e2a6c8bf652e1ad2d6a86", - "sha256:eae543b3e9253ff702103333aabd87736b5ed5e46ab834d8e0b929f08f494dee", - "sha256:f0af656402b73ead2d9f593c2774c04b01e2d0c63e4f99e0dc2f3fde99be22b4" + "sha256:0f2e739c582e8c5e432130648bc6d66a56bc65f4cd9ff0bc7033033d2130c7a3", + "sha256:0f3d159ad6cb9cbd188c726f87485f0799a067a0a15f34c25d7b5c8db3cb2e50", + "sha256:167a6aff9bd124a3a67e0ec25d0da5ecdc8d96a56405e3e5e7d586c4105eb1bb", + "sha256:1b90d50bc7a31e9573a8da1b80fcd1e4d9c86c0e5f76387858e1b87eb8b0332b", + "sha256:2baf1213ae2fd678991f905d7b2b94eddfdfb5f75757db0f0b31eebd48ca200d", + "sha256:312dda54c7e809c20d7409418060ae0e9cdbe82975e7ced429eb3c234ffc0d4a", + "sha256:32384e675f7cefe707cac40a95eeb142d6869065e39c5500374116297cd8ca6d", + "sha256:5c50634dd8f2f866fd99fd939292ce10e52bef82804ebc4e7f915221c3b7e951", + "sha256:6841bb9cc24751dbdf94e7eefc4e6d70ec297952501954471299fd12ab67391c", + "sha256:68c1c846dd267cd7e293d3fc0bb238db0a744aa1f2e721e327598f00cb982098", + "sha256:703910aaa1dcd25a412f78a190fb7a352d9a64ee7d9a35566d786f3cc66ebf20", + "sha256:8002959146ed21959e3118c60c8e94ceac02eea15b691da6c62cff4787c63f7f", + "sha256:889eef049d38488b5b4646c48a831feed37c0fd44f3d83c05cff80f4baded145", + "sha256:8c76983c9ec3e4cf3a4c1d172ec4285332d9fb1c7194d724aff0c518437471ee", + "sha256:9cd9bd72f4a9743ef6f11f0f96784bd215a542e996db1717d4c2d3d03eb81a1b", + "sha256:a1a5517301dc8d56243a14253d231ec755b94486b4fff2ae68269bc941bb1f2e", + "sha256:a2b08aec2eacae868723136383d9eb84a33062a7a7ec5ec3bd2c423bd1355946", + "sha256:a8529a79233f3581a66984acd16bce52ab0163f6f77568dd69e9ee4956d2e1db", + "sha256:afbc81a3870739610a9f9a1197374d6a45892cf1933c90fc5617d39790991ed3", + "sha256:baeb5dd8b21c718580687f5b4efd03f8139b1c56239cdf6b9805c6946e80f268", + "sha256:db1d49b753e6e6c76585f21d09c7e9812176732baa9bddb64bc2fc6cd24d4179", + "sha256:e242ed419aeb2488e0f9ee6410a34917f0f8d62b3ae96aa3170d83bae75004e2", + "sha256:e36a8857be2c849e54009f1bee25e8c34fbc683fcd38c6c700af4cba5f8d57c2", + "sha256:e699232fd033ef0053efec2cba0a7505514f374ba7b18c732a77cb5304311ef9", + "sha256:eae3da9231d87980f8082d181c276a04f7a6fdac130cebd467390b96dd05f944", + "sha256:ee6814c94dbf1cae569302afef9dd29efafc52373e8770ded0db549a3b6e0c00", + "sha256:f01a87a015227d8af407161eb48222fc3c8b01661cdc841e2b86eee4f1a7a417" ], "index": "pypi", - "version": "==4.1.2.30" + "version": "==4.2.0.32" }, "pandas": { "hashes": [ - "sha256:00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d", - "sha256:22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e", - "sha256:255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b", - "sha256:26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7", - "sha256:33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2", - "sha256:4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9", - "sha256:52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4", - "sha256:61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0", - "sha256:6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71", - "sha256:7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3", - "sha256:78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b", - "sha256:8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f", - "sha256:975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17", - "sha256:9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d", - "sha256:adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a", - "sha256:bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf", - "sha256:df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133", - "sha256:e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7", - "sha256:ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c" + "sha256:07c1b58936b80eafdfe694ce964ac21567b80a48d972879a359b3ebb2ea76835", + "sha256:0ebe327fb088df4d06145227a4aa0998e4f80a9e6aed4b61c1f303bdfdf7c722", + "sha256:11c7cb654cd3a0e9c54d81761b5920cdc86b373510d829461d8f2ed6d5905266", + "sha256:12f492dd840e9db1688126216706aa2d1fcd3f4df68a195f9479272d50054645", + "sha256:167a1315367cea6ec6a5e11e791d9604f8e03f95b57ad227409de35cf850c9c5", + "sha256:1a7c56f1df8d5ad8571fa251b864231f26b47b59cbe41aa5c0983d17dbb7a8e4", + "sha256:1fa4bae1a6784aa550a1c9e168422798104a85bf9c77a1063ea77ee6f8452e3a", + "sha256:32f42e322fb903d0e189a4c10b75ba70d90958cc4f66a1781ed027f1a1d14586", + "sha256:387dc7b3c0424327fe3218f81e05fc27832772a5dffbed385013161be58df90b", + "sha256:6597df07ea361231e60c00692d8a8099b519ed741c04e65821e632bc9ccb924c", + "sha256:743bba36e99d4440403beb45a6f4f3a667c090c00394c176092b0b910666189b", + "sha256:858a0d890d957ae62338624e4aeaf1de436dba2c2c0772570a686eaca8b4fc85", + "sha256:863c3e4b7ae550749a0bb77fa22e601a36df9d2905afef34a6965bed092ba9e5", + "sha256:a210c91a02ec5ff05617a298ad6f137b9f6f5771bf31f2d6b6367d7f71486639", + "sha256:ca84a44cf727f211752e91eab2d1c6c1ab0f0540d5636a8382a3af428542826e", + "sha256:d234bcf669e8b4d6cbcd99e3ce7a8918414520aeb113e2a81aeb02d0a533d7f7" ], "index": "pypi", - "version": "==0.25.3" + "version": "==1.0.3" }, "pandas-ods-reader": { "hashes": [ @@ -551,10 +538,10 @@ }, "pdftotext": { "hashes": [ - "sha256:b56f6ff1a564803ab8d849b3bb350b27087c15f5fe4e542a6370645543b0adf9" + "sha256:d37864049581fb13cdcf7b23d4ea23dac7ca2e9c646e8ecac1a39275ab1cae03" ], "index": "pypi", - "version": "==2.1.3" + "version": "==2.1.4" }, "pillow": { "hashes": [ @@ -586,26 +573,26 @@ }, "progressbar2": { "hashes": [ - "sha256:7538d02045a1fd3aa2b2834bfda463da8755bd3ff050edc6c5ddff3bc616215f", - "sha256:eb774d1e0d03ea4730f381c13c2c6ae7abb5ddfb14d8321d7a58a61aa708f0d0" + "sha256:2c21c14482016162852c8265da03886c2b4dea6f84e5a817ad9b39f6bd82a772", + "sha256:7849b84c01a39e4eddd2b369a129fed5e24dfb78d484ae63f9e08e58277a2928" ], - "version": "==3.47.0" + "version": "==3.50.1" }, "psutil": { "hashes": [ - "sha256:094f899ac3ef72422b7e00411b4ed174e3c5a2e04c267db6643937ddba67a05b", - "sha256:10b7f75cc8bd676cfc6fa40cd7d5c25b3f45a0e06d43becd7c2d2871cbb5e806", - "sha256:1b1575240ca9a90b437e5a40db662acd87bbf181f6aa02f0204978737b913c6b", - "sha256:21231ef1c1a89728e29b98a885b8e0a8e00d09018f6da5cdc1f43f988471a995", - "sha256:28f771129bfee9fc6b63d83a15d857663bbdcae3828e1cb926e91320a9b5b5cd", - "sha256:70387772f84fa5c3bb6a106915a2445e20ac8f9821c5914d7cbde148f4d7ff73", - "sha256:b560f5cd86cf8df7bcd258a851ca1ad98f0d5b8b98748e877a0aec4e9032b465", - "sha256:b74b43fecce384a57094a83d2778cdfc2e2d9a6afaadd1ebecb2e75e0d34e10d", - "sha256:e85f727ffb21539849e6012f47b12f6dd4c44965e56591d8dec6e8bc9ab96f4a", - "sha256:fd2e09bb593ad9bdd7429e779699d2d47c1268cbde4dda95fcd1bd17544a0217", - "sha256:ffad8eb2ac614518bbe3c0b8eb9dffdb3a8d2e3a7d5da51c5b974fb723a5c5aa" + "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058", + "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953", + "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4", + "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e", + "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f", + "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38", + "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e", + "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8", + "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26", + "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5", + "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310" ], - "version": "==5.6.7" + "version": "==5.7.0" }, "pybgpranking": { "editable": true, @@ -615,83 +602,80 @@ }, "pycparser": { "hashes": [ - "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" + "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", + "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], - "version": "==2.19" + "version": "==2.20" }, "pycryptodome": { "hashes": [ - "sha256:042ae873baadd0c33b4d699a5c5b976ade3233a979d972f98ca82314632d868c", - "sha256:0502876279772b1384b660ccc91563d04490d562799d8e2e06b411e2d81128a9", - "sha256:2de33ed0a95855735d5a0fc0c39603314df9e78ee8bbf0baa9692fb46b3b8bbb", - "sha256:319e568baf86620b419d53063b18c216abf924875966efdfe06891b987196a45", - "sha256:4372ec7518727172e1605c0843cdc5375d4771e447b8148c787b860260aae151", - "sha256:48821950ffb9c836858d8fa09d7840b6df52eadd387a3c5acece55cb387743f9", - "sha256:4b9533d4166ca07abdd49ce9d516666b1df944997fe135d4b21ac376aa624aff", - "sha256:54456cf85130e01674d21fb1ab89ffccacb138a8ade88d72fa2b0ac898d2798b", - "sha256:56fdd0e425f1b8fd3a00b6d96351f86226674974814c50534864d0124d48871f", - "sha256:57b1b707363490c495ad0eeb38bd1b0e1697c497af25fad78d3a1ebf0477fd5b", - "sha256:5c485ed6e9718ebcaa81138fa70ace9c563d202b56a8cee119b4085b023931f5", - "sha256:63c103a22cbe9752f6ea9f1a0de129995bad91c4d03a66c67cffcf6ee0c9f1e1", - "sha256:68fab8455efcbfe87c5d75015476f9b606227ffe244d57bfd66269451706e899", - "sha256:6c2720696b10ae356040e888bde1239b8957fe18885ccf5e7b4e8dec882f0856", - "sha256:72166c2ac520a5dbd2d90208b9c279161ec0861662a621892bd52fb6ca13ab91", - "sha256:7c52308ac5b834331b2f107a490b2c27de024a229b61df4cdc5c131d563dfe98", - "sha256:87d8d85b4792ca5e730fb7a519fbc3ed976c59dcf79c5204589c59afd56b9926", - "sha256:896e9b6fd0762aa07b203c993fbbee7a1f1a4674c6886afd7bfa86f3d1be98a8", - "sha256:8a799bea3c6617736e914a2e77c409f52893d382f619f088f8a80e2e21f573c1", - "sha256:9d9945ac8375d5d8e60bd2a2e1df5882eaa315522eedf3ca868b1546dfa34eba", - "sha256:9ef966c727de942de3e41aa8462c4b7b4bca70f19af5a3f99e31376589c11aac", - "sha256:a168e73879619b467072509a223282a02c8047d932a48b74fbd498f27224aa04", - "sha256:a30f501bbb32e01a49ef9e09ca1260e5ab49bf33a257080ec553e08997acc487", - "sha256:a8ca2450394d3699c9f15ef25e8de9a24b401933716a1e39d37fa01f5fe3c58b", - "sha256:aec4d42deb836b8fb3ba32f2ba1ef0d33dd3dc9d430b1479ee7a914490d15b5e", - "sha256:b4af098f2a50f8d048ab12cabb59456585c0acf43d90ee79782d2d6d0ed59dba", - "sha256:b55c60c321ac91945c60a40ac9896ac7a3d432bb3e8c14006dfd82ad5871c331", - "sha256:c53348358408d94869059e16fba5ff3bef8c52c25b18421472aba272b9bb450f", - "sha256:cbfd97f9e060f0d30245cd29fa267a9a84de9da97559366fca0a3f7655acc63f", - "sha256:d3fe3f33ad52bf0c19ee6344b695ba44ffbfa16f3c29ca61116b48d97bd970fb", - "sha256:e3a79a30d15d9c7c284a7734036ee8abdb5ca3a6f5774d293cdc9e1358c1dc10", - "sha256:eec0689509389f19875f66ae8dedd59f982240cdab31b9f78a8dc266011df93a" + "sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f", + "sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad", + "sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2", + "sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04", + "sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65", + "sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a", + "sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0", + "sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8", + "sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36", + "sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40", + "sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8", + "sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35", + "sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a", + "sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520", + "sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a", + "sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862", + "sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324", + "sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343", + "sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557", + "sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e", + "sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4", + "sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd", + "sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439", + "sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a", + "sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd", + "sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476", + "sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95", + "sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed", + "sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2", + "sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5" ], - "version": "==3.9.4" + "version": "==3.9.7" }, "pycryptodomex": { "hashes": [ - "sha256:0943b65fb41b7403a9def6214061fdd9ab9afd0bbc581e553c72eebe60bded36", - "sha256:0a1dbb5c4d975a4ea568fb7686550aa225d94023191fb0cca8747dc5b5d77857", - "sha256:0f43f1608518347fdcb9c8f443fa5cabedd33f94188b13e4196a3a7ba90d169c", - "sha256:11ce5fec5990e34e3981ed14897ba601c83957b577d77d395f1f8f878a179f98", - "sha256:17a09e38fdc91e4857cf5a7ce82f3c0b229c3977490f2146513e366923fc256b", - "sha256:22d970cee5c096b9123415e183ae03702b2cd4d3ba3f0ced25c4e1aba3967167", - "sha256:2a1793efcbae3a2264c5e0e492a2629eb10d895d6e5f17dbbd00eb8b489c6bda", - "sha256:30a8a148a0fe482cec1aaf942bbd0ade56ec197c14fe058b2a94318c57e1f991", - "sha256:32fbbaf964c5184d3f3e349085b0536dd28184b02e2b014fc900f58bbc126339", - "sha256:347d67faee36d449dc9632da411cc318df52959079062627f1243001b10dc227", - "sha256:45f4b4e5461a041518baabc52340c249b60833aa84cea6377dc8016a2b33c666", - "sha256:4717daec0035034b002d31c42e55431c970e3e38a78211f43990e1b7eaf19e28", - "sha256:51a1ac9e7dda81da444fed8be558a60ec88dfc73b2aa4b0efa310e87acb75838", - "sha256:53e9dcc8f14783f6300b70da325a50ac1b0a3dbaee323bd9dc3f71d409c197a1", - "sha256:5519a2ed776e193688b7ddb61ab709303f6eb7d1237081e298283c72acc44271", - "sha256:583450e8e80a0885c453211ed2bd69ceea634d8c904f23ff8687f677fe810e95", - "sha256:60f862bd2a07133585a4fc2ce2b1a8ec24746b07ac44307d22ef2b767cb03435", - "sha256:612091f1d3c84e723bec7cb855cf77576e646045744794c9a3f75ba80737762f", - "sha256:629a87b87c8203b8789ccefc7f2f2faecd2daaeb56bdd0b4e44cd89565f2db07", - "sha256:6e56ec4c8938fb388b6f250ddd5e21c15e8f25a76e0ad0e2abae9afee09e67b4", - "sha256:8e8092651844a11ec7fa534395f3dfe99256ce4edca06f128efc9d770d6e1dc1", - "sha256:8f5f260629876603e08f3ce95c8ccd9b6b83bf9a921c41409046796267f7adc5", - "sha256:9a6b74f38613f54c56bd759b411a352258f47489bbefd1d57c930a291498b35b", - "sha256:a5a13ebb52c4cd065fb673d8c94f39f30823428a4de19e1f3f828b63a8882d1e", - "sha256:a77ca778a476829876a3a70ae880073379160e4a465d057e3c4e1c79acdf1b8a", - "sha256:a9f7be3d19f79429c2118fd61bc2ec4fa095e93b56fb3a5f3009822402c4380f", - "sha256:dc15a467c4f9e4b43748ba2f97aea66f67812bfd581818284c47cadc81d4caec", - "sha256:e13cdeea23059f7577c230fd580d2c8178e67ebe10e360041abe86c33c316f1c", - "sha256:e45b85c8521bca6bdfaf57e4987743ade53e9f03529dd3adbc9524094c6d55c4", - "sha256:e87f17867b260f57c88487f943eb4d46c90532652bb37046e764842c3b66cbb1", - "sha256:ee40a5b156f6c1192bc3082e9d73d0479904433cdda83110546cd67f5a15a5be", - "sha256:ef63ffde3b267043579af8830fc97fc3b9b8a526a24e3ba23af9989d4e9e689a" + "sha256:1537d2d15b604b303aef56e7f440895a1c81adbee786b91f1f06eddc34da5314", + "sha256:1d20ab8369b7558168fc014a0745c678613f9f486dae468cca2d68145196b8a4", + "sha256:1ecc9db7409db67765eb008e558879d298406642d33ade43a6488224d23e8081", + "sha256:37033976f72af829fe15f7fe5fe1dbed308cc43a98d9dd9d2a0a76de8ca5ee78", + "sha256:3c3dd9d4c9c1e279d3945ae422895c901f98987333acc132dc094faf52afec35", + "sha256:3c9b3fba037ea52c626060c5a87ee6de7e86c99e8a7c6ee07302539985d2bd64", + "sha256:45ee555fc5e28c119a46d44ce373f5237e54a35c61b750fb3a94446b09855dbc", + "sha256:4c93038ac011b36512cb0bf2ee3e2aec774e8bc81021d015917c89fe02bb0ee5", + "sha256:50163324834edd0c9ce3e4512ded3e221c969086e10fdd5d3fdcaadac5e24a78", + "sha256:59b0ea9cda5490f924771456912a225d8d9e678891f9f986661af718534719b2", + "sha256:5cf306a17cccc327a33cdc3845629fa13f4573a4ec620ed607c79cf6785f2e27", + "sha256:5fff8da399af16a1855f58771223acbbdac720b9969cd03fc5013d2e9a7bd9a4", + "sha256:68650ce5b9f7152b8283302a4617269f821695a612692640dd247bd12ab21c0b", + "sha256:6b3a9a562688996f760b5077714c3ab8b62ca56061b6e9ab7906841e43e19f91", + "sha256:7e938ed51a59e29431ea86fab60423ada2757728db0f78952329fa02a789bd31", + "sha256:87aa70daad6f039e814790a06422a3189311198b674b62f13933a2bdcb6b1bcc", + "sha256:99be3a1df2b2b9f731ebe1c264a2c07c465e71cee68e35e1640b645b5213a755", + "sha256:a3f2908666e6f74b8c4893f86dd02e16170f50e4a78ae7f3468b6208d54bc205", + "sha256:ae3d44a639fd11dbdeca47e35e94febb1ee8bc15daf26673331add37146e0b85", + "sha256:afb4c2fa3c6f492fd9a8b38d76e13f32d429b8e5e1e00238309391b5591cde0d", + "sha256:b1515ce3a8a2c3fa537d137c5ca5f8b7a902044d04e07d7c3aa26c3e026120fb", + "sha256:bf391b377413a197000b43ef2b74359974d8927d329a897c9f5ba7b63dca7b9c", + "sha256:c436919117c23355740c669f89720673578b9aa4569bbfe105f6c10101fc1966", + "sha256:d2c3c280975638e2a2c2fd9cb36ab111980219757fa163a2755594b9448e4138", + "sha256:e585d530764c459cbd5d460aed0288807bb881f376ca9a20e653645217895961", + "sha256:e76e6638ead4a7d93262a24218f0ff3ff74de6b6c823b7e19dccb31b6a481978", + "sha256:ebfc2f885cafda076c31ae30fa0dd81e7e919ec34059a88d3018ed66e83fcce3", + "sha256:f5797a39933a3d41526da60856735e6684b2b71a8ca99d5f79555ca121be2f4b", + "sha256:f7e5fc5e124200b19a14be33fb0099e956e6ebb5e25d287b0829ef0a78ed76c7", + "sha256:fb350e31e55211fec8ddc89fc0256f3b9bc3b44b68a8bde1cf44b3b4e80c0e42" ], - "version": "==3.9.4" + "version": "==3.9.7" }, "pydeep": { "hashes": [ @@ -736,12 +720,12 @@ "fileobjects,openioc,virustotal,pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "3ee7d8c67601bee658f1c0f488635796e5d7eb04" + "ref": "b5b40ae2c5225a4b349c26294cfc012309a61352" }, "pyonyphe": { "editable": true, "git": "https://github.com/sebdraven/pyonyphe", - "ref": "cbb0168d5cb28a9f71f7ab3773164a7039ccdb12" + "ref": "1ce15581beebb13e841193a08a2eb6f967855fcb" }, "pyopenssl": { "hashes": [ @@ -759,11 +743,11 @@ }, "pypdns": { "hashes": [ - "sha256:349ab1033e34a60fa0c4626b3432f5202c174656955fdf330986380c9a97cf3e", - "sha256:c609678d47255a240c1e3f29a757355f610a8394ec22f21a07853360ebee6f20" + "sha256:640a7e08c3e1e6d6cf378bc7bf48225d847a9c86583c196994fb15acc20ec6f4", + "sha256:9cd2d42ed5e9e4ff7ea29b3947b133a74b0fe0f548ca4c9fac26c0b8f8b750d5" ], "index": "pypi", - "version": "==1.4.1" + "version": "==1.5.1" }, "pypssl": { "hashes": [ @@ -774,16 +758,16 @@ }, "pyrsistent": { "hashes": [ - "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280" + "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" ], - "version": "==0.15.7" + "version": "==0.16.0" }, "pytesseract": { "hashes": [ - "sha256:03735b242439f8dbedc0f33ac9d0e980d755d19ed5e51dda1dcd866d9422edc8" + "sha256:1041f83ad3eed768df145d85275bb9a611861d31fcfe30aa4bfeb79d6529b452" ], "index": "pypi", - "version": "==0.3.1" + "version": "==0.3.3" }, "python-dateutil": { "hashes": [ @@ -815,10 +799,10 @@ }, "python-utils": { "hashes": [ - "sha256:34aaf26b39b0b86628008f2ae0ac001b30e7986a8d303b61e1357dfcdad4f6d3", - "sha256:e25f840564554eaded56eaa395bca507b0b9e9f0ae5ecb13a8cb785305c56d25" + "sha256:ebaadab29d0cb9dca0a82eab9c405f5be5125dbbff35b8f32cc433fa498dbaa7", + "sha256:f21fc09ff58ea5ebd1fd2e8ef7f63e39d456336900f26bdc9334a03a3f7d8089" ], - "version": "==2.3.0" + "version": "==2.4.0" }, "pytz": { "hashes": [ @@ -829,19 +813,19 @@ }, "pyyaml": { "hashes": [ - "sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6", - "sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf", - "sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5", - "sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e", - "sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811", - "sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e", - "sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d", - "sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20", - "sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689", - "sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994", - "sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615" + "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", + "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", + "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", + "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", + "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", + "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", + "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", + "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", + "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", + "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", + "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" ], - "version": "==5.3" + "version": "==5.3.1" }, "pyzbar": { "hashes": [ @@ -869,55 +853,67 @@ }, "redis": { "hashes": [ - "sha256:3613daad9ce5951e426f460deddd5caf469e08a3af633e9578fc77d362becf62", - "sha256:8d0fc278d3f5e1249967cba2eb4a5632d19e45ce5c09442b8422d15ee2c22cc2" + "sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f", + "sha256:b205cffd05ebfd0a468db74f0eedbff8df1a7bfc47521516ade4692991bb0833" ], - "version": "==3.3.11" + "version": "==3.4.1" }, "reportlab": { "hashes": [ - "sha256:149f0eeb4ea716441638b05fd6d3667d32f1463f3eac50b63e100a73a5533cdd", - "sha256:1aa9a2e1a87749db265b592ad25e498b39f70fce9f53a012cdf69f74259b6e43", - "sha256:1f5ce489adb2db2862249492e6367539cfa65b781cb06dcf13363dc52219be7e", - "sha256:23b28ba1784a6c52a926c075abd9f396d03670e71934b24db5ff684f8b870e0f", - "sha256:3d3de0f4facdd7e3c56ecbc55733a958b86c35a8e7ba6066c7b1ba383e282f58", - "sha256:484d346b8f463ba2ddaf6d365c6ac5971cd062528b6d5ba68cac02b9435366c5", - "sha256:4da2467def21f2e20720b21f6c18e7f7866720a955c716b990e94e3979fe913f", - "sha256:5ebdf22daee7d8e630134d94f477fe6abd65a65449d4eec682a7b458b5249604", - "sha256:655a1b68be18a73fec5233fb5d81f726b4db32269e487aecf5b6853cca926d86", - "sha256:6c535a304888dafe50c2c24d4924aeefc11e0542488ee6965f6133d415e86bbc", - "sha256:7560ef655ac6448bb257fd34bfdfb8d546f9c7c0900ed8963fb8509f75e8ca80", - "sha256:7a1c2fa3e6310dbe47efee2020dc0f25be7a75ff09a8fedc4a87d4397f3810c1", - "sha256:817c344b9aa53b5bfc2f58ff82111a1e85ca4c8b68d1add088b547360a6ebcfa", - "sha256:81d950e398d6758aeaeeb267aa1a62940735414c980f77dd0a270cef1782a43d", - "sha256:83ef44936ef4e9c432d62bc2b72ec8d772b87af319d123e827a72e9b6884c851", - "sha256:9f975adc2c7a236403f0bc91d7a3916e644e47b1f1e3990325f15e73b83581ec", - "sha256:a5ca59e2b7e70a856de6db9dadd3e11a1b3b471c999585284d5c1d479c01cf5d", - "sha256:ad2cf5a673c05fae9e91e987994b95205c13c5fa55d7393cf8b06f9de6f92990", - "sha256:b8c3d76276372f87b7c8ff22065dbc072cca5ffb06ba0267edc298df7acf942d", - "sha256:b93f7f908e916d9413dd8c04da1ccb3977e446803f59078424decdc0de449133", - "sha256:c0ecd0af92c759edec0d24ba92f4a18c28d4a19229ae7c8249f94e82f3d76288", - "sha256:c9e38eefc90a02c072a87a627ff66b2d67c23f6f82274d2aa7fb28e644e8f409", - "sha256:ca2a1592d2e181a04372d0276ee847308ea206dfe7c86fe94769e7ac126e6e85", - "sha256:ce1dfc9beec83e66250ca3afaf5ddf6b9a3ce70a30a9526dec7c6bec3266baf1", - "sha256:d3550c90751132b26b72a78954905974f33b1237335fbe0d8be957f9636c376a", - "sha256:e35a574f4e5ec0fdd5dc354e74ec143d853abd7f76db435ffe2a57d0161a22eb", - "sha256:ee5cafca6ef1a38fef8cbf3140dd2198ad1ee82331530b546039216ef94f93cb", - "sha256:fa1c969176cb3594a785c6818bcb943ebd49453791f702380b13a35fa23b385a" + "sha256:072da175f9586fd0457242d7eb4ccf8284b65f8c4ec33ec4fa39c511ca2c6e10", + "sha256:12b1deee658b6a9766e7aca061dfa52c396e984fb328178480ae11ff7717cda4", + "sha256:28c56f85900bc9632ac6c44f71629a34da3a7da0904a19ecbf69ea7aec976bf3", + "sha256:2ac6bf19ecc60149895273932910b7cde61bcfc6701326094078eee489265de5", + "sha256:31feebbfd476201e82aecf750201acb1ea7d3b29217d2e0ca0a297d1189a78af", + "sha256:330aa2b493c9a42b28c65b5b4c7de4c4f372b1292f082b1a097d56b12e2ba097", + "sha256:39ae8212a07a18f0e3ee0a3bca6e5a37abac470f934e5a1a117209f989618373", + "sha256:3af29daf6681fb1c6abbe8a948c6cdf241c7d9bcdce4b881076323e70b44865c", + "sha256:3d33f934e13263fac098672840f8e0959643b747a516a50792868c3ae7251c37", + "sha256:3ea95bcfcba08eb4030e3b62efc01ff9e547eea7887311f00685c729cabce038", + "sha256:45f4aab315f301b4c184f1ee5fb4234fd1388335b191cf827ea977a98b0158dc", + "sha256:497c8d56d2f98561b78d9e21d9a2a39ab9e2dd81db699f1cddcba744ba455330", + "sha256:4f4463f1591cf66996a292835f04a521470cf9a479724017a9227125f49f7492", + "sha256:553658b979b3e8dd662cd8c37d1955cc832b2c000f4cb6d076d8401d771dd85f", + "sha256:5a8430eed5fc7d15c868fdf5673c94440710e7d1a77ea5bbd4f634e3e6fb5f9c", + "sha256:5cc32b8ce94c9345fe59af2cbf47edb1c1615304b67f522957666485f87694f7", + "sha256:5d851a20981e6ea29b643e59807997ca96ceeded4bf431ba9618171d8e383091", + "sha256:64f7cfa75b9b9a1eebf2a3fe5667a01953e1cb8946b0d14f165b9381ec2fdbaf", + "sha256:650ec96cc3cb86ae27987db5d36abe530ef45ec67032c4633c776dd3ab016ca4", + "sha256:6771e0875203d130f1f9c9c04f26084178cb4720552580af8b393cf70c4943a5", + "sha256:67f5b94ba44a4e764974b0ee9d2f574c593c11ec1cb19aedd17a1bebc35a597e", + "sha256:6d6815a925c071a0b887c968d39527e9b3db962a151d2aabdd954beafd4431ad", + "sha256:6e6e3041b742a73c71c0dc49875524338998cbf6a498077e40d4589f8448f3ed", + "sha256:6fb58a2fdc725a601d225f377b3e1cc3837f8f560cc6c2ceeb8028010031fd65", + "sha256:7c36e52452147e64a48a05ac56340b45aa3f0c64f2b2e38145ea15190c369621", + "sha256:8194698254932234a1164694a5b8c84d8010db6ff71a8985c6133d21ed9767ea", + "sha256:9c21f202697a6cea57b9d716288fc919d99cbabeb30222eebfc7ff77eac32744", + "sha256:9ffbdbac35c084c2026c4d978498017b5433a61adfe6c1e500c506d38707b39c", + "sha256:ab6acd99073081d708339e26475e93fe48139233a2ab7f43fc54560e1e00155a", + "sha256:bd1c855249f5508a50e3ddc7b4e957e4a537597bd41e66e71bdc027bbcfa7534", + "sha256:c14de6b939ad2ea63e4149e3e4eae1089e20afae1ef805345f73193f25ac9e5f", + "sha256:cb24edd3e659c783abee1162559cc2a94537974fc73d73da7e3a7021b1ab9803", + "sha256:d144680292a868cbfe02db25eecbf53623af02e42ff05822439f1434156e7863", + "sha256:db5c44a77f10357f5c2c25545b7fbc009616274f9ac1876b00398693d0fc4324", + "sha256:e326b2d48ccaf17322f86c23cd78900e50facf27b93ce50e4a2902a5f31ac343", + "sha256:e6c3fc2866b853b6b9d4b5d79cfff89c5687fc70a155a05dcfdd278747d441db", + "sha256:ef817701f45bb6974cfc0a488fd9a76c4190948c456234490174d1f2112b0a2c", + "sha256:eff08b53ab4fa2adf4b763e56dd1369d6c1cb2a18d3daee7a5f53b25198c0a36", + "sha256:f18ad0212b7204f5fae37682ec4760a11e1130c294294cfcd900d202d90ed9d9", + "sha256:f7e4e8adc959dd65e127ae0865fb278d40b34ee2ae8e41e2c5fa8dc83cea273b" ], "index": "pypi", - "version": "==3.5.32" + "version": "==3.5.42" }, "requests": { "extras": [ "security" ], "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" ], "index": "pypi", - "version": "==2.22.0" + "version": "==2.23.0" }, "requests-cache": { "hashes": [ @@ -928,25 +924,25 @@ }, "shodan": { "hashes": [ - "sha256:ed3c38c749a5d77c935b226b6a7761e972269bd0d55c5c08526af73896aa6edd" + "sha256:a9f098c2d24cf685b6d4a4bd46c7f56653c84f777f20d1a853cfd7672f68f35d" ], "index": "pypi", - "version": "==1.21.2" + "version": "==1.22.0" }, "sigmatools": { "hashes": [ - "sha256:2331bc1c6bd8e69ff3e201e51552328794f6cfc3597004fa0865341748750737", - "sha256:4361515fb8d6c6389cc0d1e5057b1f7d4cec11b8fb814e561253c01050efa634" + "sha256:6b28b30efbaa5cbb967927ea4e31c617cc91a210aad6e0a00cbe11d4ea48c3cd", + "sha256:85dfae6479d245e7e7936f02d754954ea16e2c2f757035d0b329571fa048febc" ], "index": "pypi", - "version": "==0.15.0" + "version": "==0.16.0" }, "six": { "hashes": [ - "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", - "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" ], - "version": "==1.13.0" + "version": "==1.14.0" }, "socketio-client": { "hashes": [ @@ -956,10 +952,10 @@ }, "soupsieve": { "hashes": [ - "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5", - "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda" + "sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae", + "sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69" ], - "version": "==1.9.5" + "version": "==2.0" }, "sparqlwrapper": { "hashes": [ @@ -972,28 +968,32 @@ }, "stix2-patterns": { "hashes": [ - "sha256:a23c707e8043a7933f2858adb02e58f3bace510d331e4b7dd4f1c3cceb6c43b6" + "sha256:587a82545680311431e5610036dd6c8c247347a24243fafdafaae2df4d6d7799", + "sha256:7fcb2fa67efeac2a8c493d367c93d0ce6243a10e2eff715ae9f2983e6b32b95d" ], "index": "pypi", - "version": "==1.2.1" + "version": "==1.3.0" }, "tabulate": { "hashes": [ - "sha256:5470cc6687a091c7042cee89b2946d9235fe9f6d49c193a4ae2ac7bf386737c8" + "sha256:ac64cb76d53b1231d364babcd72abbb16855adac7de6665122f97b593f1eb2ba", + "sha256:db2723a20d04bcda8522165c73eea7c300eda74e0ce852d9022e0159d7895007" ], - "version": "==0.8.6" + "version": "==0.8.7" }, "tornado": { "hashes": [ - "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", - "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", - "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", - "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", - "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", - "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", - "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5" + "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc", + "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52", + "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6", + "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d", + "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b", + "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673", + "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9", + "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a", + "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740" ], - "version": "==6.0.3" + "version": "==6.0.4" }, "url-normalize": { "hashes": [ @@ -1011,10 +1011,10 @@ }, "urllib3": { "hashes": [ - "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", - "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" + "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", + "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" ], - "version": "==1.25.7" + "version": "==1.25.8" }, "uwhois": { "editable": true, @@ -1047,11 +1047,11 @@ }, "wand": { "hashes": [ - "sha256:46a1eb1ec092d5954d0f5e88ee216e87d9e8b7d28d36a21c342a5b13ebb6604e", - "sha256:6d0925190a846e28412814ea50fa8b3d7969859bac8a93ebc5b2f1c0a1a34d6a" + "sha256:598e13e46779e48fcecba7b37fd9d61fcdd1e70007ccba5d5b2e731186a2ec2e", + "sha256:6eaca78e53fbe329b163f0f0b28f104de98edbd69a847268cc5d6a6e392b9b28" ], "index": "pypi", - "version": "==0.5.8" + "version": "==0.5.9" }, "websocket-client": { "hashes": [ @@ -1062,9 +1062,9 @@ }, "wrapt": { "hashes": [ - "sha256:565a021fd19419476b9362b05eeaa094178de64f8361e44468f9e9d7843901e1" + "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" ], - "version": "==1.11.2" + "version": "==1.12.1" }, "xlrd": { "hashes": [ @@ -1076,10 +1076,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:18fe8f891a4adf7556c05d56059e136f9fbce5b19f9335f6d7b42c389c4592bc", - "sha256:5d3630ff9b2a277c939bd5053d0e7466499593abebbab9ce1dc9b1481a8ebbb6" + "sha256:488e1988ab16ff3a9cd58c7656d0a58f8abe46ee58b98eecea78c022db28656b", + "sha256:97ab487b81534415c5313154203f3e8a637d792b1e6a8201e8f7f71da0203c2a" ], - "version": "==1.2.7" + "version": "==1.2.8" }, "yara-python": { "hashes": [ @@ -1122,10 +1122,10 @@ }, "zipp": { "hashes": [ - "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", - "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" + "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", + "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" ], - "version": "==0.6.0" + "version": "==3.1.0" } }, "develop": { @@ -1152,47 +1152,47 @@ }, "codecov": { "hashes": [ - "sha256:8ed8b7c6791010d359baed66f84f061bba5bd41174bf324c31311e8737602788", - "sha256:ae00d68e18d8a20e9c3288ba3875ae03db3a8e892115bf9b83ef20507732bed4" + "sha256:09fb045eb044a619cd2b9dacd7789ae8e322cb7f18196378579fd8d883e6b665", + "sha256:aeeefa3a03cac8a78e4f988e935b51a4689bb1f17f20d4e827807ee11135f845" ], "index": "pypi", - "version": "==2.0.15" + "version": "==2.0.22" }, "coverage": { "hashes": [ - "sha256:189aac76d6e0d7af15572c51892e7326ee451c076c5a50a9d266406cd6c49708", - "sha256:1bf7ba2af1d373a1750888724f84cffdfc697738f29a353c98195f98fc011509", - "sha256:1f4ee8e2e4243971618bc16fcc4478317405205f135e95226c2496e2a3b8dbbf", - "sha256:225e79a5d485bc1642cb7ba02281419c633c216cdc6b26c26494ba959f09e69f", - "sha256:23688ff75adfa8bfa2a67254d889f9bdf9302c27241d746e17547c42c732d3f4", - "sha256:28f7f73b34a05e23758e860a89a7f649b85c6749e252eff60ebb05532d180e86", - "sha256:2d0cb9b1fe6ad0d915d45ad3d87f03a38e979093a98597e755930db1f897afae", - "sha256:47874b4711c5aeb295c31b228a758ce3d096be83dc37bd56da48ed99efb8813b", - "sha256:511ec0c00840e12fb4e852e4db58fa6a01ca4da72f36a9766fae344c3d502033", - "sha256:53e7438fef0c97bc248f88ba1edd10268cd94d5609970aaf87abbe493691af87", - "sha256:569f9ee3025682afda6e9b0f5bb14897c0db03f1a1dc088b083dd36e743f92bb", - "sha256:593853aa1ac6dcc6405324d877544c596c9d948ef20d2e9512a0f5d2d3202356", - "sha256:5b0a07158360d22492f9abd02a0f2ee7981b33f0646bf796598b7673f6bbab14", - "sha256:7ca3db38a61f3655a2613ee2c190d63639215a7a736d3c64cc7bbdb002ce6310", - "sha256:7d1cc7acc9ce55179616cf72154f9e648136ea55987edf84addbcd9886ffeba2", - "sha256:88b51153657612aea68fa684a5b88037597925260392b7bb4509d4f9b0bdd889", - "sha256:955ec084f549128fa2702f0b2dc696392001d986b71acd8fd47424f28289a9c3", - "sha256:b251c7092cbb6d789d62dc9c9e7c4fb448c9138b51285c36aeb72462cad3600e", - "sha256:bd82b684bb498c60ef47bb1541a50e6d006dde8579934dcbdbc61d67d1ea70d9", - "sha256:bfe102659e2ec13b86c7f3b1db6c9a4e7beea4255058d006351339e6b342d5d2", - "sha256:c1e4e39e43057396a5e9d069bfbb6ffeee892e40c5d2effbd8cd71f34ee66c4d", - "sha256:cb2b74c123f65e8166f7e1265829a6c8ed755c3cd16d7f50e75a83456a5f3fd7", - "sha256:cca38ded59105f7705ef6ffe1e960b8db6c7d8279c1e71654a4775ab4454ca15", - "sha256:cf908840896f7aa62d0ec693beb53264b154f972eb8226fb864ac38975590c4f", - "sha256:d095a7b473f8a95f7efe821f92058c8a2ecfb18f8db6677ae3819e15dc11aaae", - "sha256:d22b4297e7e4225ccf01f1aa55e7a96412ea0796b532dd614c3fcbafa341128e", - "sha256:d4a2b578a7a70e0c71f662705262f87a456f1e6c1e40ada7ea699abaf070a76d", - "sha256:ddeb42a3d5419434742bf4cc71c9eaa22df3b76808e23a82bd0b0bd360f1a9f1", - "sha256:e65a5aa1670db6263f19fdc03daee1d7dbbadb5cb67fd0a1f16033659db13c1d", - "sha256:eaad65bd20955131bcdb3967a4dea66b4e4d4ca488efed7c00d91ee0173387e8", - "sha256:f45fba420b94165c17896861bb0e8b27fb7abdcedfeb154895d8553df90b7b00" + "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0", + "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30", + "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b", + "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0", + "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823", + "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe", + "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037", + "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6", + "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31", + "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd", + "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892", + "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1", + "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78", + "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac", + "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006", + "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014", + "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2", + "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7", + "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8", + "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7", + "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9", + "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1", + "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307", + "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a", + "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435", + "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0", + "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5", + "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441", + "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732", + "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de", + "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1" ], - "version": "==5.0.2" + "version": "==5.0.4" }, "entrypoints": { "hashes": [ @@ -1211,18 +1211,18 @@ }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" ], - "version": "==2.8" + "version": "==2.9" }, "importlib-metadata": { "hashes": [ - "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", - "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" + "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", + "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" ], "markers": "python_version < '3.8'", - "version": "==1.3.0" + "version": "==1.6.0" }, "mccabe": { "hashes": [ @@ -1233,10 +1233,10 @@ }, "more-itertools": { "hashes": [ - "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", - "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" + "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c", + "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507" ], - "version": "==8.0.2" + "version": "==8.2.0" }, "nose": { "hashes": [ @@ -1249,10 +1249,10 @@ }, "packaging": { "hashes": [ - "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", - "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" + "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3", + "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752" ], - "version": "==20.0" + "version": "==20.3" }, "pluggy": { "hashes": [ @@ -1291,50 +1291,50 @@ }, "pytest": { "hashes": [ - "sha256:6b571215b5a790f9b41f19f3531c53a45cf6bb8ef2988bc1ff9afb38270b25fa", - "sha256:e41d489ff43948babd0fad7ad5e49b8735d5d55e26628a58673c39ff61d95de4" + "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172", + "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970" ], "index": "pypi", - "version": "==5.3.2" + "version": "==5.4.1" }, "requests": { "extras": [ "security" ], "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" ], "index": "pypi", - "version": "==2.22.0" + "version": "==2.23.0" }, "six": { "hashes": [ - "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", - "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" ], - "version": "==1.13.0" + "version": "==1.14.0" }, "urllib3": { "hashes": [ - "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", - "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" + "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", + "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" ], - "version": "==1.25.7" + "version": "==1.25.8" }, "wcwidth": { "hashes": [ - "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", - "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" + "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", + "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" ], - "version": "==0.1.8" + "version": "==0.1.9" }, "zipp": { "hashes": [ - "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", - "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" + "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", + "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" ], - "version": "==0.6.0" + "version": "==3.1.0" } } } diff --git a/REQUIREMENTS b/REQUIREMENTS index 40b4caf..f98ae8b 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -3,29 +3,29 @@ -e git+https://github.com/D4-project/BGP-Ranking.git/@fd9c0e03af9b61d4bf0b67ac73c7208a55178a54#egg=pybgpranking&subdirectory=client -e git+https://github.com/D4-project/IPASN-History.git/@fc5e48608afc113e101ca6421bf693b7b9753f9e#egg=pyipasnhistory&subdirectory=client -e git+https://github.com/MISP/PyIntel471.git@0df8d51f1c1425de66714b3a5a45edb69b8cc2fc#egg=pyintel471 --e git+https://github.com/MISP/PyMISP.git@a26a8e450b14d48bb0c8ef46b32bff2f1eadc514#egg=pymisp[fileobjects,openioc,virustotal,pdfexport] +-e git+https://github.com/MISP/PyMISP.git@b5b40ae2c5225a4b349c26294cfc012309a61352#egg=pymisp[fileobjects,openioc,virustotal,pdfexport] -e git+https://github.com/Rafiot/uwhoisd.git@411572840eba4c72dc321c549b36a54ed5cea9de#egg=uwhois&subdirectory=client -e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader -e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails --e git+https://github.com/sebdraven/pyonyphe@cbb0168d5cb28a9f71f7ab3773164a7039ccdb12#egg=pyonyphe +-e git+https://github.com/sebdraven/pyonyphe@1ce15581beebb13e841193a08a2eb6f967855fcb#egg=pyonyphe aiohttp==3.4.4 -antlr4-python3-runtime==4.7.2 ; python_version >= '3' +antlr4-python3-runtime==4.8 ; python_version >= '3' apiosintds==1.8.3 argparse==1.4.0 assemblyline-client==3.7.3 async-timeout==3.0.1 attrs==19.3.0 backscatter==0.2.4 -beautifulsoup4==4.8.1 +beautifulsoup4==4.8.2 blockchain==1.4.4 certifi==2019.11.28 -cffi==1.13.2 +cffi==1.14.0 chardet==3.0.4 click-plugins==1.1.1 -click==7.0 +click==7.1.1 colorama==0.4.3 cryptography==2.8 -decorator==4.4.1 +decorator==4.4.2 deprecated==1.2.7 dnspython==1.16.0 domaintools-api==0.3.3 @@ -33,77 +33,77 @@ enum-compat==0.0.3 ez-setup==0.9 ezodf==0.3.2 future==0.18.2 -geoip2==2.9.0 -httplib2==0.14.0 +futures==3.1.1 +geoip2==3.0.0 +httplib2==0.17.0 idna-ssl==1.1.0 ; python_version < '3.7' -idna==2.8 -importlib-metadata==1.3.0 ; python_version < '3.8' +idna==2.9 +importlib-metadata==1.6.0 ; python_version < '3.8' isodate==0.6.0 jbxapi==3.4.0 jsonschema==3.2.0 lief==0.10.1 -lxml==4.4.2 +lxml==4.5.0 maclookup==1.0.3 -maxminddb==1.5.1 -more-itertools==8.0.2 -multidict==4.7.1 +maxminddb==1.5.2 +multidict==4.7.5 np==1.0.2 -numpy==1.17.4 +numpy==1.18.2 oauth2==1.9.0.post1 -opencv-python==4.1.2.30 +opencv-python==4.2.0.32 pandas-ods-reader==0.0.7 -pandas==0.25.3 +pandas==1.0.3 passivetotal==1.0.31 -pdftotext==2.1.2 -pillow==6.2.1 -progressbar2==3.47.0 -psutil==5.6.7 -pycparser==2.19 -pycryptodome==3.9.4 -pycryptodomex==3.9.4 +pdftotext==2.1.4 +pillow==7.0.0 +progressbar2==3.50.1 +psutil==5.7.0 +pycparser==2.20 +pycryptodome==3.9.7 +pycryptodomex==3.9.7 pydeep==0.4 pyeupi==1.0 pygeoip==0.3.2 pyopenssl==19.1.0 -pyparsing==2.4.5 -pypdns==1.4.1 +pyparsing==2.4.6 +pypdns==1.5.1 pypssl==2.1 -pyrsistent==0.15.6 -pytesseract==0.3.0 +pyrsistent==0.16.0 +pytesseract==0.3.3 python-dateutil==2.8.1 python-docx==0.8.10 python-magic==0.4.15 python-pptx==0.6.18 -python-utils==2.3.0 +python-utils==2.4.0 pytz==2019.3 -pyyaml==5.2 +pyyaml==5.3.1 pyzbar==0.1.8 pyzipper==0.3.1 ; python_version >= '3.5' rdflib==4.2.2 -redis==3.3.11 -reportlab==3.5.32 +redis==3.4.1 +reportlab==3.5.42 requests-cache==0.5.2 -requests[security]==2.22.0 -shodan==1.21.0 -sigmatools==0.15.0 -six==1.13.0 +requests[security]==2.23.0 +shodan==1.22.0 +sigmatools==0.16.0 +six==1.14.0 socketio-client==0.5.6 -soupsieve==1.9.5 -sparqlwrapper==1.8.4 -stix2-patterns==1.2.1 -tabulate==0.8.6 -tornado==6.0.3 +soupsieve==2.0 +sparqlwrapper==1.8.5 +stix2-patterns==1.3.0 +tabulate==0.8.7 +tornado==6.0.4 url-normalize==1.4.1 urlarchiver==0.2 -urllib3==1.25.7 +urllib3==1.25.8 validators==0.14.0 -vulners==1.5.4 -wand==0.5.8 -websocket-client==0.56.0 -wrapt==1.11.2 +vt-graph-api==1.0.1 +vulners==1.5.5 +wand==0.5.9 +websocket-client==0.57.0 +wrapt==1.12.1 xlrd==1.2.0 -xlsxwriter==1.2.6 +xlsxwriter==1.2.8 yara-python==3.8.1 yarl==1.4.2 -zipp==0.6.0 -vt-graph-api +zipp==3.1.0 From b79636ccfa18a2334b55acacae52737b2052be7f Mon Sep 17 00:00:00 2001 From: Golbark Date: Fri, 3 Apr 2020 03:15:03 -0700 Subject: [PATCH 119/220] new: usr: Censys Expansion module --- README.md | 1 + REQUIREMENTS | 1 + doc/expansion/censys_enrich.py | 8 + misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/censys_enrich.py | 179 ++++++++++++++++++ 5 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 doc/expansion/censys_enrich.py create mode 100644 misp_modules/modules/expansion/censys_enrich.py diff --git a/README.md b/README.md index fe37cd5..db199a0 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](https://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. +* [Censys-enrich](misp_modules/modules/expansion/censys_enrich.py) - An expansion and module to retrieve information from censys.io about a particular IP or certificate. * [CIRCL Passive DNS](misp_modules/modules/expansion/circl_passivedns.py) - a hover and expansion module to expand hostname and IP addresses with passive DNS information. * [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate(s) seen. * [countrycode](misp_modules/modules/expansion/countrycode.py) - a hover module to tell you what country a URL belongs to. diff --git a/REQUIREMENTS b/REQUIREMENTS index f98ae8b..c69b383 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -18,6 +18,7 @@ attrs==19.3.0 backscatter==0.2.4 beautifulsoup4==4.8.2 blockchain==1.4.4 +censys==0.0.8 certifi==2019.11.28 cffi==1.14.0 chardet==3.0.4 diff --git a/doc/expansion/censys_enrich.py b/doc/expansion/censys_enrich.py new file mode 100644 index 0000000..83e6d5f --- /dev/null +++ b/doc/expansion/censys_enrich.py @@ -0,0 +1,8 @@ +{ + "description": "An expansion module to enrich attributes in MISP by quering the censys.io API", + "requirements": ["API credentials to censys.io"], + "input": "IP, domain or certificate fingerprint (md5, sha1 or sha256)", + "output": "MISP objects retrieved from censys, including open ports, ASN, Location of the IP, x509 details", + "references": ["https://www.censys.io"], + "features": "This module takes an IP, hostname or a certificate fingerprint and attempts to enrich it by querying the Censys API." +} diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 2a99050..82264fa 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -16,4 +16,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', - 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion'] + 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich'] diff --git a/misp_modules/modules/expansion/censys_enrich.py b/misp_modules/modules/expansion/censys_enrich.py new file mode 100644 index 0000000..e3f2acd --- /dev/null +++ b/misp_modules/modules/expansion/censys_enrich.py @@ -0,0 +1,179 @@ +# encoding: utf-8 +import json +import base64 +import codecs +from dateutil.parser import isoparse +from pymisp import MISPAttribute, MISPEvent, MISPObject +try: + import censys.base + import censys.ipv4 + import censys.websites + import censys.certificates +except ImportError: + print("Censys module not installed. Try 'pip install censys'") + +misperrors = {'error': 'Error'} +moduleconfig = ['api_id', 'api_secret'] +mispattributes = {'input': ['ip-src', 'ip-dst', 'domain', 'x509-fingerprint-md5', 'x509-fingerprint-sha1', 'x509-fingerprint-sha256'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.1', 'author': 'Loïc Fortemps', + 'description': 'Censys.io expansion module', 'module-type': ['expansion', 'hover']} + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + + if request.get('config'): + if (request['config'].get('api_id') is None) or (request['config'].get('api_secret') is None): + misperrors['error'] = "Censys API credentials are missing" + return misperrors + else: + misperrors['error'] = "Please provide config options" + return misperrors + + api_id = request['config']['api_id'] + api_secret = request['config']['api_secret'] + + if not request.get('attribute'): + return {'error': 'Unsupported input.'} + attribute = request['attribute'] + if not any(input_type == attribute['type'] for input_type in mispattributes['input']): + return {'error': 'Unsupported attributes type'} + + attribute = MISPAttribute() + attribute.from_dict(**request['attribute']) + + if attribute.type == 'ip-dst' or attribute.type == 'ip-src': + conn = censys.ipv4.CensysIPv4(api_id=api_id, api_secret=api_secret) + elif attribute.type == 'domain': + conn = censys.websites.CensysWebsites(api_id=api_id, api_secret=api_secret) + elif 'x509-fingerprint' in attribute.type: + conn = censys.certificates.CensysCertificates(api_id=api_id, api_secret=api_secret) + else: + return False + + try: + result = conn.view(attribute.value) + except censys.base.CensysNotFoundException: + misperrors['error'] = "Nothing could be found on Censys" + return misperrors + except Exception: + misperrors['error'] = "Connection issue" + return misperrors + + r = {'results': parse_response(result, attribute)} + return r + + +def parse_response(censys_output, attribute): + misp_event = MISPEvent() + misp_event.add_attribute(**attribute) + # Generic fields (for IP/Websites) + if "autonomous_system" in censys_output: + cen_as = censys_output['autonomous_system'] + asn_object = MISPObject('asn') + asn_object.add_attribute('asn', value=cen_as["asn"]) + asn_object.add_attribute('description', value=cen_as['name']) + asn_object.add_attribute('subnet-announced', value=cen_as['routed_prefix']) + asn_object.add_attribute('country', value=cen_as['country_code']) + asn_object.add_reference(attribute.uuid, 'associated-to') + misp_event.add_object(**asn_object) + + if "ip" in censys_output and "ports" in censys_output: + ip_object = MISPObject('ip-port') + ip_object.add_attribute('ip', value=censys_output['ip']) + for p in censys_output['ports']: + ip_object.add_attribute('dst-port', value=p) + ip_object.add_reference(attribute.uuid, 'associated-to') + misp_event.add_object(**ip_object) + + # We explore all ports to find https or ssh services + for k in censys_output.keys(): + if not isinstance(censys_output[k], dict): + continue + if 'https' in censys_output[k]: + try: + cert = censys_output[k]['https']['tls']['certificate'] + cert_obj = get_certificate_object(cert, attribute) + misp_event.add_object(**cert_obj) + except KeyError: + print("Error !") + if 'ssh' in censys_output[k]: + try: + cert = censys_output[k]['ssh']['v2']['server_host_key'] + # To enable once the type is merged + # misp_event.add_attribute(type='hasshserver-sha256', value=cert['fingerprint_sha256']) + except KeyError: + pass + + # Info from certificate query + if "parsed" in censys_output: + cert_obj = get_certificate_object(censys_output, attribute) + misp_event.add_object(**cert_obj) + + # Location can be present for IP/Websites results + if "location" in censys_output: + loc_obj = MISPObject('geolocation') + loc = censys_output['location'] + loc_obj.add_attribute('latitude', value=loc['latitude']) + loc_obj.add_attribute('longitude', value=loc['longitude']) + loc_obj.add_attribute('city', value=loc['city']) + loc_obj.add_attribute('country', value=loc['country']) + loc_obj.add_attribute('zipcode', value=loc['postal_code']) + if 'province' in loc: + loc_obj.add_attribute('region', value=loc['province']) + loc_obj.add_reference(attribute.uuid, 'associated-to') + misp_event.add_object(**loc_obj) + + event = json.loads(misp_event.to_json()) + return {'Object': event['Object'], 'Attribute': event['Attribute']} + + +def get_certificate_object(cert, attribute): + parsed = cert['parsed'] + cert_object = MISPObject('x509') + cert_object.add_attribute('x509-fingerprint-sha256', value=parsed['fingerprint_sha256']) + cert_object.add_attribute('x509-fingerprint-sha1', value=parsed['fingerprint_sha1']) + cert_object.add_attribute('x509-fingerprint-md5', value=parsed['fingerprint_md5']) + cert_object.add_attribute('serial-number', value=parsed['serial_number']) + cert_object.add_attribute('version', value=parsed['version']) + cert_object.add_attribute('subject', value=parsed['subject_dn']) + cert_object.add_attribute('issuer', value=parsed['issuer_dn']) + cert_object.add_attribute('validity-not-before', value=isoparse(parsed['validity']['start'])) + cert_object.add_attribute('validity-not-after', value=isoparse(parsed['validity']['end'])) + cert_object.add_attribute('self_signed', value=parsed['signature']['self_signed']) + cert_object.add_attribute('signature_algorithm', value=parsed['signature']['signature_algorithm']['name']) + + cert_object.add_attribute('pubkey-info-algorithm', value=parsed['subject_key_info']['key_algorithm']['name']) + + if 'rsa_public_key' in parsed['subject_key_info']: + pub_key = parsed['subject_key_info']['rsa_public_key'] + cert_object.add_attribute('pubkey-info-size', value=pub_key['length']) + cert_object.add_attribute('pubkey-info-exponent', value=pub_key['exponent']) + hex_mod = codecs.encode(base64.b64decode(pub_key['modulus']), 'hex').decode() + cert_object.add_attribute('pubkey-info-modulus', value=hex_mod) + + if "extensions" in parsed and "subject_alt_name" in parsed["extensions"]: + san = parsed["extensions"]["subject_alt_name"] + if "dns_names" in san: + for dns in san['dns_names']: + cert_object.add_attribute('dns_names', value=dns) + if "ip_addresses" in san: + for ip in san['ip_addresses']: + cert_object.add_attribute('ip', value=ip) + + if "raw" in cert: + cert_object.add_attribute('raw-base64', value=cert['raw']) + + cert_object.add_reference(attribute.uuid, 'associated-to') + return cert_object + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 500f0301a97917482cb52409bdd36914d3fe6ce5 Mon Sep 17 00:00:00 2001 From: Golbark Date: Tue, 7 Apr 2020 06:53:42 -0700 Subject: [PATCH 120/220] Adding support for more input types, including multi-types --- .../modules/expansion/censys_enrich.py | 112 +++++++++++++++--- 1 file changed, 93 insertions(+), 19 deletions(-) diff --git a/misp_modules/modules/expansion/censys_enrich.py b/misp_modules/modules/expansion/censys_enrich.py index e3f2acd..b1b89b8 100644 --- a/misp_modules/modules/expansion/censys_enrich.py +++ b/misp_modules/modules/expansion/censys_enrich.py @@ -14,7 +14,8 @@ except ImportError: misperrors = {'error': 'Error'} moduleconfig = ['api_id', 'api_secret'] -mispattributes = {'input': ['ip-src', 'ip-dst', 'domain', 'x509-fingerprint-md5', 'x509-fingerprint-sha1', 'x509-fingerprint-sha256'], 'format': 'misp_standard'} +mispattributes = {'input': ['ip-src', 'ip-dst', 'domain', 'hostname', 'hostname|port', 'domain|ip', 'ip-dst|port', 'ip-src|port', + 'x509-fingerprint-md5', 'x509-fingerprint-sha1', 'x509-fingerprint-sha256'], 'format': 'misp_standard'} moduleinfo = {'version': '0.1', 'author': 'Loïc Fortemps', 'description': 'Censys.io expansion module', 'module-type': ['expansion', 'hover']} @@ -43,27 +44,53 @@ def handler(q=False): attribute = MISPAttribute() attribute.from_dict(**request['attribute']) + # Lists to accomodate multi-types attribute + conn = list() + types = list() + values = list() + results = list() - if attribute.type == 'ip-dst' or attribute.type == 'ip-src': - conn = censys.ipv4.CensysIPv4(api_id=api_id, api_secret=api_secret) - elif attribute.type == 'domain': - conn = censys.websites.CensysWebsites(api_id=api_id, api_secret=api_secret) - elif 'x509-fingerprint' in attribute.type: - conn = censys.certificates.CensysCertificates(api_id=api_id, api_secret=api_secret) + if "|" in attribute.type: + t_1, t_2 = attribute.type.split('|') + v_1, v_2 = attribute.value.split('|') + # We cannot use the port information + if t_2 == "port": + types.append(t_1) + values.append(v_1) + else: + types = [t_1, t_2] + values = [v_1, v_2] else: - return False + types.append(attribute.type) + values.append(attribute.value) - try: - result = conn.view(attribute.value) - except censys.base.CensysNotFoundException: + for t in types: + # ip, ip-src or ip-dst + if t[:2] == "ip": + conn.append(censys.ipv4.CensysIPv4(api_id=api_id, api_secret=api_secret)) + elif t == 'domain' or t == "hostname": + conn.append(censys.websites.CensysWebsites(api_id=api_id, api_secret=api_secret)) + elif 'x509-fingerprint' in t: + conn.append(censys.certificates.CensysCertificates(api_id=api_id, api_secret=api_secret)) + + found = True + for c in conn: + val = values.pop(0) + try: + r = c.view(val) + results.append(parse_response(r, attribute)) + found = True + except censys.base.CensysNotFoundException: + found = False + except Exception: + misperrors['error'] = "Connection issue" + return misperrors + + if not found: misperrors['error'] = "Nothing could be found on Censys" return misperrors - except Exception: - misperrors['error'] = "Connection issue" - return misperrors - r = {'results': parse_response(result, attribute)} - return r + return {'results': remove_duplicates(results)} def parse_response(censys_output, attribute): @@ -102,7 +129,7 @@ def parse_response(censys_output, attribute): if 'ssh' in censys_output[k]: try: cert = censys_output[k]['ssh']['v2']['server_host_key'] - # To enable once the type is merged + # TODO enable once the type is merged # misp_event.add_attribute(type='hasshserver-sha256', value=cert['fingerprint_sha256']) except KeyError: pass @@ -118,9 +145,11 @@ def parse_response(censys_output, attribute): loc = censys_output['location'] loc_obj.add_attribute('latitude', value=loc['latitude']) loc_obj.add_attribute('longitude', value=loc['longitude']) - loc_obj.add_attribute('city', value=loc['city']) + if 'city' in loc: + loc_obj.add_attribute('city', value=loc['city']) loc_obj.add_attribute('country', value=loc['country']) - loc_obj.add_attribute('zipcode', value=loc['postal_code']) + if 'postal_code' in loc: + loc_obj.add_attribute('zipcode', value=loc['postal_code']) if 'province' in loc: loc_obj.add_attribute('region', value=loc['province']) loc_obj.add_reference(attribute.uuid, 'associated-to') @@ -130,6 +159,51 @@ def parse_response(censys_output, attribute): return {'Object': event['Object'], 'Attribute': event['Attribute']} +# In case of multiple enrichment (ip and domain), we need to filter out similar objects +# TODO: make it more granular +def remove_duplicates(results): + # Only one enrichment was performed so no duplicate + if len(results) == 1: + return results[0] + elif len(results) == 2: + final_result = results[0] + obj_l2 = results[1]['Object'] + for o2 in obj_l2: + if o2['name'] == "asn": + key = "asn" + elif o2['name'] == "ip-port": + key = "ip" + elif o2['name'] == "x509": + key = "x509-fingerprint-sha256" + elif o2['name'] == "geolocation": + key = "latitude" + if not check_if_present(o2, key, final_result['Object']): + final_result['Object'].append(o2) + + return final_result + else: + return [] + + +def check_if_present(object, attribute_name, list_objects): + """ + Assert if a given object is present in the list. + + This function check if object (json format) is present in list_objects + using attribute_name for the matching + """ + for o in list_objects: + if o['name'] == object['name']: + for attr in object['Attribute']: + if attr['type'] == attribute_name: + value = attr['value'] + for attr2 in o['Attribute']: + if attr['type'] == attribute_name and attr['value'] == value: + return True + + return False + + def get_certificate_object(cert, attribute): parsed = cert['parsed'] cert_object = MISPObject('x509') From fd3c62c460dc52c18881a983a13b73f56774a063 Mon Sep 17 00:00:00 2001 From: Golbark Date: Wed, 8 Apr 2020 01:07:46 -0700 Subject: [PATCH 121/220] Fix variable issue in the loop --- misp_modules/modules/expansion/censys_enrich.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/misp_modules/modules/expansion/censys_enrich.py b/misp_modules/modules/expansion/censys_enrich.py index b1b89b8..0fc61ae 100644 --- a/misp_modules/modules/expansion/censys_enrich.py +++ b/misp_modules/modules/expansion/censys_enrich.py @@ -193,13 +193,15 @@ def check_if_present(object, attribute_name, list_objects): using attribute_name for the matching """ for o in list_objects: + # We first look for a match on the name if o['name'] == object['name']: for attr in object['Attribute']: + # Within the attributes, we look for the one to compare if attr['type'] == attribute_name: - value = attr['value'] - for attr2 in o['Attribute']: - if attr['type'] == attribute_name and attr['value'] == value: - return True + # Then we check the attributes of the other object and look for a match + for attr2 in o['Attribute']: + if attr2['type'] == attribute_name and attr2['value'] == attr['value']: + return True return False From be2786990346f8f5b12812cc0572bf51ba291f7c Mon Sep 17 00:00:00 2001 From: Christophe Vandeplas Date: Wed, 8 Apr 2020 11:46:59 +0200 Subject: [PATCH 122/220] fix: [doc] corrected filenames for 2 docs --- doc/README.md | 34 +++++++++++++++++++ .../{censys_enrich.py => censys_enrich.json} | 0 .../{cytomic_orion.py => cytomic_orion.json} | 0 3 files changed, 34 insertions(+) rename doc/expansion/{censys_enrich.py => censys_enrich.json} (100%) rename doc/expansion/{cytomic_orion.py => cytomic_orion.json} (100%) diff --git a/doc/README.md b/doc/README.md index 7e6bee3..37cb2c9 100644 --- a/doc/README.md +++ b/doc/README.md @@ -152,6 +152,22 @@ An expansion hover module to get a blockchain balance from a BTC address in MISP ----- +#### [censys_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/censys_enrich.py) + +An expansion module to enrich attributes in MISP by quering the censys.io API +- **features**: +>This module takes an IP, hostname or a certificate fingerprint and attempts to enrich it by querying the Censys API. +- **input**: +>IP, domain or certificate fingerprint (md5, sha1 or sha256) +- **output**: +>MISP objects retrieved from censys, including open ports, ASN, Location of the IP, x509 details +- **references**: +>https://www.censys.io +- **requirements**: +>API credentials to censys.io + +----- + #### [circl_passivedns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/circl_passivedns.py) @@ -295,6 +311,24 @@ An expansion hover module to expand information about CVE id. ----- +#### [cytomic_orion](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cytomic_orion.py) + + + +An expansion module to enrich attributes in MISP by quering the Cytomic Orion API +- **features**: +>This module takes an MD5 hash and searches for occurrences of this hash in the Cytomic Orion database. Returns observed files and machines. +- **input**: +>MD5, hash of the sample / malware to search for. +- **output**: +>MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines. +- **references**: +>https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/, https://www.cytomicmodel.com/solutions/ +- **requirements**: +>Access (license) to Cytomic Orion + +----- + #### [dbl_spamhaus](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/dbl_spamhaus.py) diff --git a/doc/expansion/censys_enrich.py b/doc/expansion/censys_enrich.json similarity index 100% rename from doc/expansion/censys_enrich.py rename to doc/expansion/censys_enrich.json diff --git a/doc/expansion/cytomic_orion.py b/doc/expansion/cytomic_orion.json similarity index 100% rename from doc/expansion/cytomic_orion.py rename to doc/expansion/cytomic_orion.json From ebf71a371b67fcdfcd7f8548d135716c9286d90d Mon Sep 17 00:00:00 2001 From: Matthias Meidinger Date: Thu, 23 Apr 2020 14:47:48 +0200 Subject: [PATCH 123/220] Update vmray_submit The submit module hat some smaller issues with the reanalyze flag. The source for the enrichment object has been changed and the robustness of user supplied config parsing improved. --- .../modules/expansion/vmray_submit.py | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/misp_modules/modules/expansion/vmray_submit.py b/misp_modules/modules/expansion/vmray_submit.py index 4d34c4b..73a0cdf 100644 --- a/misp_modules/modules/expansion/vmray_submit.py +++ b/misp_modules/modules/expansion/vmray_submit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ''' -Submit sample to VMRay. +Submit sample to VMRay. Requires "vmray_rest_api" @@ -14,6 +14,7 @@ as a cron job import json import base64 +from distutils.util import strtobool import io import zipfile @@ -22,7 +23,7 @@ from ._vmray.vmray_rest_api import VMRayRESTAPI misperrors = {'error': 'Error'} mispattributes = {'input': ['attachment', 'malware-sample'], 'output': ['text', 'sha1', 'sha256', 'md5', 'link']} -moduleinfo = {'version': '0.2', 'author': 'Koen Van Impe', +moduleinfo = {'version': '0.3', 'author': 'Koen Van Impe', 'description': 'Submit a sample to VMRay', 'module-type': ['expansion']} moduleconfig = ['apikey', 'url', 'shareable', 'do_not_reanalyze', 'do_not_include_vmrayjobids'] @@ -71,25 +72,13 @@ def handler(q=False): do_not_reanalyze = request["config"].get("do_not_reanalyze") do_not_include_vmrayjobids = request["config"].get("do_not_include_vmrayjobids") - # Do we want the sample to be shared? - if shareable == "True": - shareable = True - else: - shareable = False - - # Always reanalyze the sample? - if do_not_reanalyze == "True": - do_not_reanalyze = True - else: - do_not_reanalyze = False - reanalyze = not do_not_reanalyze - - # Include the references to VMRay job IDs - if do_not_include_vmrayjobids == "True": - do_not_include_vmrayjobids = True - else: - do_not_include_vmrayjobids = False - include_vmrayjobids = not do_not_include_vmrayjobids + try: + shareable = bool(strtobool(shareable)) # Do we want the sample to be shared? + reanalyze = not bool(strtobool(do_not_reanalyze)) # Always reanalyze the sample? + include_vmrayjobids = not bool(strtobool(do_not_include_vmrayjobids)) # Include the references to VMRay job IDs + except ValueError: + misperrors["error"] = "Error while processing settings. Please double-check your values." + return misperrors if data and sample_filename: args = {} @@ -99,7 +88,7 @@ def handler(q=False): try: vmraydata = vmraySubmit(api, args) - if vmraydata["errors"]: + if vmraydata["errors"] and "Submission not stored" not in vmraydata["errors"][0]["error_msg"]: misperrors['error'] = "VMRay: %s" % vmraydata["errors"][0]["error_msg"] return misperrors else: @@ -125,22 +114,20 @@ def vmrayProcess(vmraydata): ''' Process the JSON file returned by vmray''' if vmraydata: try: - submissions = vmraydata["submissions"][0] + sample = vmraydata["samples"][0] jobs = vmraydata["jobs"] # Result received? - if submissions and jobs: + if sample: r = {'results': []} - r['results'].append({'types': 'md5', 'values': submissions['submission_sample_md5']}) - r['results'].append({'types': 'sha1', 'values': submissions['submission_sample_sha1']}) - r['results'].append({'types': 'sha256', 'values': submissions['submission_sample_sha256']}) - r['results'].append({'types': 'text', 'values': 'VMRay Sample ID: %s' % submissions['submission_sample_id'], 'tags': 'workflow:state="incomplete"'}) - r['results'].append({'types': 'text', 'values': 'VMRay Submission ID: %s' % submissions['submission_id']}) - r['results'].append({'types': 'text', 'values': 'VMRay Submission Sample IP: %s' % submissions['submission_ip_ip']}) - r['results'].append({'types': 'link', 'values': submissions['submission_webif_url']}) + r['results'].append({'types': 'md5', 'values': sample['sample_md5hash']}) + r['results'].append({'types': 'sha1', 'values': sample['sample_sha1hash']}) + r['results'].append({'types': 'sha256', 'values': sample['sample_sha256hash']}) + r['results'].append({'types': 'text', 'values': 'VMRay Sample ID: %s' % sample['sample_id'], 'tags': 'workflow:state="incomplete"'}) + r['results'].append({'types': 'link', 'values': sample['sample_webif_url']}) # Include data from different jobs - if include_vmrayjobids: + if include_vmrayjobids and len(jobs) > 0: for job in jobs: job_id = job["job_id"] job_vm_name = job["job_vm_name"] From c58f131e10670e652bb3de1fa62f78ab2997aa0a Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 07:40:05 +0900 Subject: [PATCH 124/220] chg: [travis] Added py3.8 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0b87679..b70f838 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ python: - "3.6" - "3.6-dev" - "3.7-dev" + - "3.8-dev" before_install: - docker build -t misp-modules --build-arg BUILD_DATE=$(date -u +"%Y-%m-%d") docker/ From 72913c94891f26db88d3f5a278aa4f1cd70e5ad0 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 07:53:19 +0900 Subject: [PATCH 125/220] fix: [pip] pyfaup required --- REQUIREMENTS | 1 + 1 file changed, 1 insertion(+) diff --git a/REQUIREMENTS b/REQUIREMENTS index c69b383..e749db9 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -8,6 +8,7 @@ -e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader -e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails -e git+https://github.com/sebdraven/pyonyphe@1ce15581beebb13e841193a08a2eb6f967855fcb#egg=pyonyphe +-e git+https://github.com/stricaud/faup.git#egg=pyfaup&subdirectory=src/lib/bindings/python aiohttp==3.4.4 antlr4-python3-runtime==4.8 ; python_version >= '3' apiosintds==1.8.3 From acee9888b684a27ba48500aa7b5cc94ea75650a0 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 08:45:10 +0900 Subject: [PATCH 126/220] chg: [travis] Added gtcaca and liblua to faup --- .travis.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b70f838..3031967 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,15 @@ 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 tesseract-ocr libfuzzy-dev + - sudo apt-get install libzbar0 libzbar-dev libpoppler-cpp-dev tesseract-ocr libfuzzy-dev libcaca-dev liblua5.3-dev - pip install pipenv - pipenv install --dev + # install gtcaca + - git clone git://github.com/stricaud/gtcaca.git gtcaca + - pushd gtcaca/build + - cmake .. && make + - sudo make install + - popd # install pyfaup - git clone https://github.com/stricaud/faup.git - pushd faup/build From e655905ee0aad62485534d65332f32c938476304 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 11:45:47 +0900 Subject: [PATCH 127/220] chg: [doc] in case btc expansion fails, give another hint at why it fails --- tests/test_expansions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 801769a..b853c25 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -105,9 +105,10 @@ class TestExpansions(unittest.TestCase): query = {"module": "btc_steroids", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} response = self.misp_modules_post(query) try: - self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0000000000 BTC (+0.0005355700 BTC / -0.0005355700 BTC)')) + self.assertTrue(self.get_values(response).startswith('\n\nAddress:\t1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA\nBalance:\t0.0002126800 BTC (+0.0007482500 BTC / -0.0005355700 BTC)')) + except Exception: - self.assertEqual(self.get_values(response), 'Not a valid BTC address') + self.assertEqual(self.get_values(response), 'Not a valid BTC address, or Balance has changed') def test_btc_scam_check(self): query = {"module": "btc_scam_check", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From 9f8a72ba64ea24cc02b96f27f60eac010851647c Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 11:59:33 +0900 Subject: [PATCH 128/220] fix: [travis] gtcaca has no build directory --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3031967..4d551b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,8 @@ install: - pip install pipenv - pipenv install --dev # install gtcaca - - git clone git://github.com/stricaud/gtcaca.git gtcaca + - git clone git://github.com/stricaud/gtcaca.git + - mkdir -p gtcaca/build - pushd gtcaca/build - cmake .. && make - sudo make install From dbb7d37b1e5b597d38e60b9b7580d9f723e2230a Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 12:09:18 +0900 Subject: [PATCH 129/220] chg: [doc] Added details about faup --- docs/install.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/install.md b/docs/install.md index 72cf9d6..662e675 100644 --- a/docs/install.md +++ b/docs/install.md @@ -21,8 +21,28 @@ $SUDO_WWW virtualenv -p python3 /var/www/MISP/venv # END with virtualenv cd /usr/local/src/ -sudo git clone https://github.com/MISP/misp-modules.git -cd misp-modules +# Ideally you add your user to the staff group and make /usr/local/src group writeable, below follows an example with user misp +sudo adduser misp staff +sudo chmod 2775 /usr/local/src +sudo chown root:staff /usr/local/src +git clone https://github.com/MISP/misp-modules.git +git clone git://github.com/stricaud/faup.git faup +git clone git://github.com/stricaud/gtcaca.git gtcaca + +# Install gtcaca/faup +cd gtcaca +mkdir -p build +cd build +cmake .. && make +sudo make install +cd ../../faup +mkdir -p build +cd build +cmake .. && make +sudo make install +sudo ldconfig + +cd ../../misp-modules # BEGIN with virtualenv: $SUDO_WWW /var/www/MISP/venv/bin/pip install -I -r REQUIREMENTS @@ -168,4 +188,4 @@ tar xvf misp-module-bundeled.tar.bz2 -C misp-modules-bundle cd misp-modules-bundle ls -1|while read line; do sudo pip3 install --force-reinstall --ignore-installed --upgrade --no-index --no-deps ${line};done ~~~ -Next you can follow standard install procedure. \ No newline at end of file +Next you can follow standard install procedure. From 3fd6633c015f913b088b6fc07eaf1c418474bf71 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Fri, 1 May 2020 12:12:33 +0900 Subject: [PATCH 130/220] fix: [pep] Comply to PEP E261 --- misp_modules/modules/expansion/vmray_submit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misp_modules/modules/expansion/vmray_submit.py b/misp_modules/modules/expansion/vmray_submit.py index 73a0cdf..1c0d553 100644 --- a/misp_modules/modules/expansion/vmray_submit.py +++ b/misp_modules/modules/expansion/vmray_submit.py @@ -73,9 +73,9 @@ def handler(q=False): do_not_include_vmrayjobids = request["config"].get("do_not_include_vmrayjobids") try: - shareable = bool(strtobool(shareable)) # Do we want the sample to be shared? - reanalyze = not bool(strtobool(do_not_reanalyze)) # Always reanalyze the sample? - include_vmrayjobids = not bool(strtobool(do_not_include_vmrayjobids)) # Include the references to VMRay job IDs + shareable = bool(strtobool(shareable)) # Do we want the sample to be shared? + reanalyze = not bool(strtobool(do_not_reanalyze)) # Always reanalyze the sample? + include_vmrayjobids = not bool(strtobool(do_not_include_vmrayjobids)) # Include the references to VMRay job IDs except ValueError: misperrors["error"] = "Error while processing settings. Please double-check your values." return misperrors From 6f74885056a04119a95df9f8eab0e132b8cfd859 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 May 2020 17:05:53 +0000 Subject: [PATCH 131/220] build(deps): bump httplib2 from 0.17.0 to 0.18.0 Bumps [httplib2](https://github.com/httplib2/httplib2) from 0.17.0 to 0.18.0. - [Release notes](https://github.com/httplib2/httplib2/releases) - [Changelog](https://github.com/httplib2/httplib2/blob/master/CHANGELOG) - [Commits](https://github.com/httplib2/httplib2/compare/v0.17.0...v0.18.0) Signed-off-by: dependabot[bot] --- Pipfile.lock | 323 +++++++++++++++++++++++---------------------------- 1 file changed, 146 insertions(+), 177 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index ac5749a..9a23d0d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -112,10 +112,10 @@ }, "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "cffi": { "hashes": [ @@ -159,10 +159,10 @@ }, "click": { "hashes": [ - "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", - "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], - "version": "==7.1.1" + "version": "==7.1.2" }, "click-plugins": { "hashes": [ @@ -180,29 +180,27 @@ }, "cryptography": { "hashes": [ - "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c", - "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595", - "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad", - "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651", - "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2", - "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff", - "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d", - "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42", - "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d", - "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e", - "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912", - "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793", - "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13", - "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7", - "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0", - "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879", - "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f", - "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9", - "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2", - "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf", - "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8" + "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", + "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b", + "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5", + "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf", + "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e", + "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b", + "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae", + "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b", + "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0", + "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b", + "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d", + "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229", + "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3", + "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365", + "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55", + "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270", + "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e", + "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785", + "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0" ], - "version": "==2.8" + "version": "==2.9.2" }, "decorator": { "hashes": [ @@ -213,10 +211,10 @@ }, "deprecated": { "hashes": [ - "sha256:408038ab5fdeca67554e8f6742d1521cd3cd0ee0ff9d47f29318a4f4da31c308", - "sha256:8b6a5aa50e482d8244a62e5582b96c372e87e3a28e8b49c316e46b95c76a611d" + "sha256:525ba66fb5f90b07169fdd48b6373c18f1ee12728ca277ca44567a367d9d7f74", + "sha256:a766c1dccb30c5f6eb2b203f87edd1d8588847709c78589e1521d769addc8218" ], - "version": "==1.2.7" + "version": "==1.2.10" }, "dnspython": { "hashes": [ @@ -277,10 +275,11 @@ }, "httplib2": { "hashes": [ - "sha256:79751cc040229ec896aa01dced54de0cd0bf042f928e84d5761294422dde4454", - "sha256:de96d0a49f46d0ee7e0aae80141d37b8fcd6a68fb05d02e0b82c128592dd8261" + "sha256:4f6988e6399a2546b525a037d56da34aed4d149bbdc0e78523018d5606c26e74", + "sha256:b0e1f3ed76c97380fe2485bc47f25235453b40ef33ca5921bb2897e257a49c4c" ], - "version": "==0.17.0" + "index": "pypi", + "version": "==0.18.0" }, "idna": { "hashes": [ @@ -297,14 +296,6 @@ "markers": "python_version < '3.7'", "version": "==1.1.0" }, - "importlib-metadata": { - "hashes": [ - "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", - "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" - ], - "markers": "python_version < '3.8'", - "version": "==1.6.0" - }, "isodate": { "hashes": [ "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8", @@ -388,9 +379,9 @@ }, "maxminddb": { "hashes": [ - "sha256:d0ce131d901eb11669996b49a59f410efd3da2c6dbe2c0094fe2fef8d85b6336" + "sha256:f4d28823d9ca23323d113dc7af8db2087aa4f657fafc64ff8f7a8afda871425b" ], - "version": "==1.5.2" + "version": "==1.5.4" }, "misp-modules": { "editable": true, @@ -398,25 +389,25 @@ }, "multidict": { "hashes": [ - "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1", - "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35", - "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928", - "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969", - "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e", - "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78", - "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1", - "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136", - "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8", - "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2", - "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e", - "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4", - "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5", - "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd", - "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab", - "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20", - "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3" + "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", + "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", + "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", + "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", + "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", + "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", + "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", + "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", + "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", + "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", + "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", + "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", + "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", + "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", + "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", + "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", + "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" ], - "version": "==4.7.5" + "version": "==4.7.6" }, "np": { "hashes": [ @@ -427,29 +418,29 @@ }, "numpy": { "hashes": [ - "sha256:1598a6de323508cfeed6b7cd6c4efb43324f4692e20d1f76e1feec7f59013448", - "sha256:1b0ece94018ae21163d1f651b527156e1f03943b986188dd81bc7e066eae9d1c", - "sha256:2e40be731ad618cb4974d5ba60d373cdf4f1b8dcbf1dcf4d9dff5e212baf69c5", - "sha256:4ba59db1fcc27ea31368af524dcf874d9277f21fd2e1f7f1e2e0c75ee61419ed", - "sha256:59ca9c6592da581a03d42cc4e270732552243dc45e87248aa8d636d53812f6a5", - "sha256:5e0feb76849ca3e83dd396254e47c7dba65b3fa9ed3df67c2556293ae3e16de3", - "sha256:6d205249a0293e62bbb3898c4c2e1ff8a22f98375a34775a259a0523111a8f6c", - "sha256:6fcc5a3990e269f86d388f165a089259893851437b904f422d301cdce4ff25c8", - "sha256:82847f2765835c8e5308f136bc34018d09b49037ec23ecc42b246424c767056b", - "sha256:87902e5c03355335fc5992a74ba0247a70d937f326d852fc613b7f53516c0963", - "sha256:9ab21d1cb156a620d3999dd92f7d1c86824c622873841d6b080ca5495fa10fef", - "sha256:a1baa1dc8ecd88fb2d2a651671a84b9938461e8a8eed13e2f0a812a94084d1fa", - "sha256:a244f7af80dacf21054386539699ce29bcc64796ed9850c99a34b41305630286", - "sha256:a35af656a7ba1d3decdd4fae5322b87277de8ac98b7d9da657d9e212ece76a61", - "sha256:b1fe1a6f3a6f355f6c29789b5927f8bd4f134a4bd9a781099a7c4f66af8850f5", - "sha256:b5ad0adb51b2dee7d0ee75a69e9871e2ddfb061c73ea8bc439376298141f77f5", - "sha256:ba3c7a2814ec8a176bb71f91478293d633c08582119e713a0c5351c0f77698da", - "sha256:cd77d58fb2acf57c1d1ee2835567cd70e6f1835e32090538f17f8a3a99e5e34b", - "sha256:cdb3a70285e8220875e4d2bc394e49b4988bdb1298ffa4e0bd81b2f613be397c", - "sha256:deb529c40c3f1e38d53d5ae6cd077c21f1d49e13afc7936f7f868455e16b64a0", - "sha256:e7894793e6e8540dbeac77c87b489e331947813511108ae097f1715c018b8f3d" + "sha256:00d7b54c025601e28f468953d065b9b121ddca7fff30bed7be082d3656dd798d", + "sha256:02ec9582808c4e48be4e93cd629c855e644882faf704bc2bd6bbf58c08a2a897", + "sha256:0e6f72f7bb08f2f350ed4408bb7acdc0daba637e73bce9f5ea2b207039f3af88", + "sha256:1be2e96314a66f5f1ce7764274327fd4fb9da58584eaff00b5a5221edefee7d6", + "sha256:2466fbcf23711ebc5daa61d28ced319a6159b260a18839993d871096d66b93f7", + "sha256:2b573fcf6f9863ce746e4ad00ac18a948978bb3781cffa4305134d31801f3e26", + "sha256:3f0dae97e1126f529ebb66f3c63514a0f72a177b90d56e4bce8a0b5def34627a", + "sha256:50fb72bcbc2cf11e066579cb53c4ca8ac0227abb512b6cbc1faa02d1595a2a5d", + "sha256:57aea170fb23b1fd54fa537359d90d383d9bf5937ee54ae8045a723caa5e0961", + "sha256:709c2999b6bd36cdaf85cf888d8512da7433529f14a3689d6e37ab5242e7add5", + "sha256:7d59f21e43bbfd9a10953a7e26b35b6849d888fc5a331fa84a2d9c37bd9fe2a2", + "sha256:904b513ab8fbcbdb062bed1ce2f794ab20208a1b01ce9bd90776c6c7e7257032", + "sha256:96dd36f5cdde152fd6977d1bbc0f0561bccffecfde63cd397c8e6033eb66baba", + "sha256:9933b81fecbe935e6a7dc89cbd2b99fea1bf362f2790daf9422a7bb1dc3c3085", + "sha256:bbcc85aaf4cd84ba057decaead058f43191cc0e30d6bc5d44fe336dc3d3f4509", + "sha256:dccd380d8e025c867ddcb2f84b439722cf1f23f3a319381eac45fd077dee7170", + "sha256:e22cd0f72fc931d6abc69dc7764484ee20c6a60b0d0fee9ce0426029b1c1bdae", + "sha256:ed722aefb0ebffd10b32e67f48e8ac4c5c4cf5d3a785024fdf0e9eb17529cd9d", + "sha256:efb7ac5572c9a57159cf92c508aad9f856f1cb8e8302d7fdb99061dbe52d712c", + "sha256:efdba339fffb0e80fcc19524e4fdbda2e2b5772ea46720c44eaac28096d60720", + "sha256:f22273dd6a403ed870207b853a856ff6327d5cbce7a835dfa0645b3fc00273ec" ], - "version": "==1.18.2" + "version": "==1.18.4" }, "oauth2": { "hashes": [ @@ -573,10 +564,10 @@ }, "progressbar2": { "hashes": [ - "sha256:2c21c14482016162852c8265da03886c2b4dea6f84e5a817ad9b39f6bd82a772", - "sha256:7849b84c01a39e4eddd2b369a129fed5e24dfb78d484ae63f9e08e58277a2928" + "sha256:57594cc7ff7ff93138d6c09f650f9d31290b5d3bd1cf12339ced96a50c148749", + "sha256:ecf687696dd449067f69ef6730c4d4a0189db1f8d1aad9e376358354631d5b2c" ], - "version": "==3.50.1" + "version": "==3.51.3" }, "psutil": { "hashes": [ @@ -736,10 +727,10 @@ }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "version": "==2.4.6" + "version": "==2.4.7" }, "pypdns": { "hashes": [ @@ -785,10 +776,10 @@ }, "python-magic": { "hashes": [ - "sha256:f2674dcfad52ae6c49d4803fa027809540b130db1dec928cfbb9240316831375", - "sha256:f3765c0f582d2dfc72c15f3b5a82aecfae9498bd29ca840d72f37d7bd38bfcd5" + "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", + "sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce" ], - "version": "==0.4.15" + "version": "==0.4.18" }, "python-pptx": { "hashes": [ @@ -846,17 +837,17 @@ }, "rdflib": { "hashes": [ - "sha256:58d5994610105a457cff7fdfe3d683d87786c5028a45ae032982498a7e913d6f", - "sha256:da1df14552555c5c7715d8ce71c08f404c988c58a1ecd38552d0da4fc261280d" + "sha256:78149dd49d385efec3b3adfbd61c87afaf1281c30d3fcaf1b323b34f603fb155", + "sha256:88208ea971a87886d60ae2b1a4b2cdc263527af0454c422118d43fe64b357877" ], - "version": "==4.2.2" + "version": "==5.0.0" }, "redis": { "hashes": [ - "sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f", - "sha256:b205cffd05ebfd0a468db74f0eedbff8df1a7bfc47521516ade4692991bb0833" + "sha256:2ef11f489003f151777c064c5dbc6653dfb9f3eade159bcadc524619fddc2242", + "sha256:6d65e84bc58091140081ee9d9c187aab0480097750fac44239307a3bdf0b1251" ], - "version": "==3.4.1" + "version": "==3.5.2" }, "reportlab": { "hashes": [ @@ -952,10 +943,10 @@ }, "soupsieve": { "hashes": [ - "sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae", - "sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69" + "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55", + "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232" ], - "version": "==2.0" + "version": "==2.0.1" }, "sparqlwrapper": { "hashes": [ @@ -997,10 +988,10 @@ }, "url-normalize": { "hashes": [ - "sha256:3468d64cb22a9092a2c086e46c781f741dc9a1689b24e9b48ab5e8244ffa6c02", - "sha256:51e0f14050c79e732d175c33d12167f5e642cc23e0cb23275236af843faf884f" + "sha256:1709cb4739e496f9f807a894e361915792f273538e250b1ab7da790544a665c3", + "sha256:1bd7085349dcdf06e52194d0f75ff99fff2eeed0da85a50e4cc2346452c1b8bc" ], - "version": "==1.4.1" + "version": "==1.4.2" }, "urlarchiver": { "hashes": [ @@ -1011,10 +1002,10 @@ }, "urllib3": { "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], - "version": "==1.25.8" + "version": "==1.25.9" }, "uwhois": { "editable": true, @@ -1119,13 +1110,6 @@ "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" ], "version": "==1.4.2" - }, - "zipp": { - "hashes": [ - "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", - "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" - ], - "version": "==3.1.0" } }, "develop": { @@ -1138,10 +1122,10 @@ }, "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "chardet": { "hashes": [ @@ -1160,39 +1144,39 @@ }, "coverage": { "hashes": [ - "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0", - "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30", - "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b", - "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0", - "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823", - "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe", - "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037", - "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6", - "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31", - "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd", - "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892", - "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1", - "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78", - "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac", - "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006", - "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014", - "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2", - "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7", - "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8", - "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7", - "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9", - "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1", - "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307", - "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a", - "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435", - "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0", - "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5", - "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441", - "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732", - "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de", - "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1" + "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", + "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", + "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", + "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", + "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", + "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", + "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", + "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", + "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", + "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", + "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", + "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", + "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", + "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", + "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", + "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", + "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", + "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", + "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", + "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", + "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", + "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", + "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", + "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", + "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", + "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", + "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", + "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", + "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", + "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", + "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" ], - "version": "==5.0.4" + "version": "==5.1" }, "entrypoints": { "hashes": [ @@ -1216,14 +1200,6 @@ ], "version": "==2.9" }, - "importlib-metadata": { - "hashes": [ - "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", - "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" - ], - "markers": "python_version < '3.8'", - "version": "==1.6.0" - }, "mccabe": { "hashes": [ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -1233,10 +1209,10 @@ }, "more-itertools": { "hashes": [ - "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c", - "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507" + "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be", + "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982" ], - "version": "==8.2.0" + "version": "==8.3.0" }, "nose": { "hashes": [ @@ -1249,10 +1225,10 @@ }, "packaging": { "hashes": [ - "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3", - "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752" + "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", + "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" ], - "version": "==20.3" + "version": "==20.4" }, "pluggy": { "hashes": [ @@ -1284,10 +1260,10 @@ }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "version": "==2.4.6" + "version": "==2.4.7" }, "pytest": { "hashes": [ @@ -1317,10 +1293,10 @@ }, "urllib3": { "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], - "version": "==1.25.8" + "version": "==1.25.9" }, "wcwidth": { "hashes": [ @@ -1328,13 +1304,6 @@ "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" ], "version": "==0.1.9" - }, - "zipp": { - "hashes": [ - "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", - "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" - ], - "version": "==3.1.0" } } } From 8a95a000eefd49181b3f71946aa8a79ed80ddd55 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Fri, 29 May 2020 17:21:20 -0700 Subject: [PATCH 132/220] initial commit. not a working product. need to create a class to manage the MISP event and TruStar client --- REQUIREMENTS | 1 + misp_modules/modules/expansion/__init__.py | 4 +- .../modules/expansion/trustar_enrich.py | 63 +++++++++++++++++++ .../modules/import_mod/trustar_import.py | 0 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/trustar_enrich.py create mode 100644 misp_modules/modules/import_mod/trustar_import.py diff --git a/REQUIREMENTS b/REQUIREMENTS index e749db9..73b002a 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -95,6 +95,7 @@ sparqlwrapper==1.8.5 stix2-patterns==1.3.0 tabulate==0.8.7 tornado==6.0.4 +trustar==0.3.28 url-normalize==1.4.1 urlarchiver==0.2 urllib3==1.25.8 diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 82264fa..c05804b 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -1,6 +1,7 @@ from . import _vmray # noqa import os import sys + sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/')[:-3]))) __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'circl_passivessl', @@ -16,4 +17,5 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'ods_enrich', 'odt_enrich', 'joesandbox_submit', 'joesandbox_query', 'urlhaus', 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', - 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich'] + 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', + 'trustar_enrich'] diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py new file mode 100644 index 0000000..e786ff3 --- /dev/null +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -0,0 +1,63 @@ +import json +from pymisp import MISPAttribute, MISPEvent, MISPObject +from trustar import TruStar + +misperrors = {'error': "Error"} +mispattributes = {'input': ["btc", "domain","email-src", "filename", "hostname", "ip-src", "ip-dst", "malware-type", "md5", "sha1", "sha256", "url"], 'format': 'misp_standard'} + +moduleinfo = {'version': "0.1", 'author': "Jesse Hedden", + 'description': "Enrich data with TruSTAR", + 'module-type': ["hover", "expansion"]} + +moduleconfig = ["api_key", "api_secret", "enclave_ids"] + + +def get_results(misp_event): + event = json.loads(misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} + +def parse_indicator_summary(attribute, summary): + misp_event = MISPEvent() + misp_attribute = MISPAttribute().from_dict(**attribute) + misp_event.add_attribute(**misp_attribute) + + mapping = {'value': 'text', 'reportId': 'text', 'enclaveId': 'text', 'description': 'text'} + + for item in summary.get('items'): + trustar_obj = MISPObject(attribute.value) + for key, attribute_type in mapping.items(): + trustar_obj.add_attribute(key, attribute_type=attribute_type, value=item[key]) + trustar_obj.add_reference(misp_attribute.uuid, 'associated-to') + misp_event.add_object(**trustar_obj) + + return misp_event + + +def handler(q=False): + + if q is False: + return False + + request = json.loads(q) + config = request.get('config', {}) + if not config.get('api_key') or not config.get('api_secret'): + misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." + return misperrors + + enclave_ids = [enclave_id for enclave_id in config.get('enclave_ids', "").split(',')] + ts_client = TruStar(config={'user_api_key': config.get('api_key'), 'user_api_secret': config.get('api_secret'), 'enclave_ids': enclave_ids}) + attribute = request.get('attribute') + + summary = ts_client.get_indicator_summaries(attribute) + + misp_event = parse_indicator_summary(attribute, summary) + return get_results(misp_event) + +def introspection(): + return mispattributes + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo + diff --git a/misp_modules/modules/import_mod/trustar_import.py b/misp_modules/modules/import_mod/trustar_import.py new file mode 100644 index 0000000..e69de29 From 67bdb38fc8d1e36f6e3005478be2e6498be31fd9 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Fri, 29 May 2020 17:41:13 -0700 Subject: [PATCH 133/220] WIP: initial push --- misp_modules/modules/import_mod/__init__.py | 1 + misp_modules/modules/import_mod/trustar_import.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/misp_modules/modules/import_mod/__init__.py b/misp_modules/modules/import_mod/__init__.py index fbad911..45e3359 100644 --- a/misp_modules/modules/import_mod/__init__.py +++ b/misp_modules/modules/import_mod/__init__.py @@ -15,4 +15,5 @@ __all__ = [ 'threatanalyzer_import', 'csvimport', 'joe_import', + 'trustar_import', ] diff --git a/misp_modules/modules/import_mod/trustar_import.py b/misp_modules/modules/import_mod/trustar_import.py index e69de29..2c55be2 100644 --- a/misp_modules/modules/import_mod/trustar_import.py +++ b/misp_modules/modules/import_mod/trustar_import.py @@ -0,0 +1,7 @@ +import base64 +import json + +from trustar import TruStar + +misp_errors = {'error': "Error"} + From 31d15056f9f85298eb95a5b0dac7ba0ddd8c19e7 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Wed, 3 Jun 2020 11:12:47 +0200 Subject: [PATCH 134/220] new: [passivedns, passivessl] Add support for ip-src|port and ip-dst|port --- misp_modules/modules/expansion/circl_passivedns.py | 10 ++++++---- misp_modules/modules/expansion/circl_passivessl.py | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/misp_modules/modules/expansion/circl_passivedns.py b/misp_modules/modules/expansion/circl_passivedns.py index 75ff6c6..2455be0 100755 --- a/misp_modules/modules/expansion/circl_passivedns.py +++ b/misp_modules/modules/expansion/circl_passivedns.py @@ -2,7 +2,7 @@ import json import pypdns from pymisp import MISPAttribute, MISPEvent, MISPObject -mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst'], 'format': 'misp_standard'} +mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'], 'format': 'misp_standard'} moduleinfo = {'version': '0.2', 'author': 'Alexandre Dulaunoy', 'description': 'Module to access CIRCL Passive DNS', 'module-type': ['expansion', 'hover']} @@ -24,9 +24,11 @@ class PassiveDNSParser(): results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} - def parse(self, value): + def parse(self): + value = self.attribute.value.split('|')[0] if '|' in self.attribute.type else self.attribute.value + try: - results = self.pdns.query(self.attribute.value) + results = self.pdns.query(value) except Exception: self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} return @@ -57,7 +59,7 @@ def handler(q=False): if not any(input_type == attribute['type'] for input_type in mispattributes['input']): return {'error': 'Unsupported attributes type'} pdns_parser = PassiveDNSParser(attribute, authentication) - pdns_parser.parse(attribute['value']) + pdns_parser.parse() return pdns_parser.get_results() diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index 0c11106..e43defc 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -2,7 +2,7 @@ import json import pypssl from pymisp import MISPAttribute, MISPEvent, MISPObject -mispattributes = {'input': ['ip-src', 'ip-dst'], 'format': 'misp_standard'} +mispattributes = {'input': ['ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'], 'format': 'misp_standard'} moduleinfo = {'version': '0.2', 'author': 'Raphaël Vinot', 'description': 'Module to access CIRCL Passive SSL', 'module-type': ['expansion', 'hover']} @@ -31,9 +31,11 @@ class PassiveSSLParser(): results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} - def parse(self, value): + def parse(self): + value = self.attribute.value.split('|')[0] if '|' in self.attribute.type else self.attribute.value + try: - results = self.pssl.query(self.attribute.value) + results = self.pssl.query(value) except Exception: self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} return @@ -78,7 +80,7 @@ def handler(q=False): if not any(input_type == attribute['type'] for input_type in mispattributes['input']): return {'error': 'Unsupported attributes type'} pssl_parser = PassiveSSLParser(attribute, authentication) - pssl_parser.parse(attribute['value']) + pssl_parser.parse() return pssl_parser.get_results() From 6e21893be4869bcd5874bea0e2c854996962a47b Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Wed, 3 Jun 2020 10:48:43 +0200 Subject: [PATCH 135/220] fix: [circl_passivedns] Return not found error If passivedns returns empty response, return Not found error instead of error in log --- misp_modules/modules/expansion/circl_passivedns.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/misp_modules/modules/expansion/circl_passivedns.py b/misp_modules/modules/expansion/circl_passivedns.py index 75ff6c6..ef8042d 100755 --- a/misp_modules/modules/expansion/circl_passivedns.py +++ b/misp_modules/modules/expansion/circl_passivedns.py @@ -30,6 +30,11 @@ class PassiveDNSParser(): except Exception: self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} return + + if not results: + self.result = {'error': 'Not found'} + return + mapping = {'count': 'counter', 'origin': 'text', 'time_first': 'datetime', 'rrtype': 'text', 'rrname': 'text', 'rdata': 'text', From b053e1c01b0ea8f4189d7815dd194947113b22c5 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Wed, 3 Jun 2020 11:19:21 +0200 Subject: [PATCH 136/220] fix: [circl_passivessl] Return not found error If passivessl returns empty response, return Not found error instead of error in log --- misp_modules/modules/expansion/circl_passivessl.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index 0c11106..86ded68 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -37,6 +37,11 @@ class PassiveSSLParser(): except Exception: self.result = {'error': 'There is an authentication error, please make sure you supply correct credentials.'} return + + if not results: + self.result = {'error': 'Not found'} + return + for ip_address, certificates in results.items(): ip_uuid = self._handle_ip_attribute(ip_address) for certificate in certificates['certificates']: From fe1ea90b25773463ed2e6f0e8753a464cf9d148b Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Wed, 3 Jun 2020 14:06:57 +0200 Subject: [PATCH 137/220] fix: [circl_passivessl] Return proper error for IPv6 addresses --- misp_modules/modules/expansion/circl_passivessl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index 3419bbb..102bed8 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -44,6 +44,10 @@ class PassiveSSLParser(): self.result = {'error': 'Not found'} return + if 'error' in results: + self.result = {'error': results['error']} + return + for ip_address, certificates in results.items(): ip_uuid = self._handle_ip_attribute(ip_address) for certificate in certificates['certificates']: From 341a569de54c56ddb927bc80a67f0319824db6f4 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sun, 21 Jun 2020 19:52:17 -0700 Subject: [PATCH 138/220] ready for code review --- .../modules/expansion/trustar_enrich.py | 113 ++++++++++++------ 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index e786ff3..38f5d16 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -3,61 +3,100 @@ from pymisp import MISPAttribute, MISPEvent, MISPObject from trustar import TruStar misperrors = {'error': "Error"} -mispattributes = {'input': ["btc", "domain","email-src", "filename", "hostname", "ip-src", "ip-dst", "malware-type", "md5", "sha1", "sha256", "url"], 'format': 'misp_standard'} +mispattributes = { + 'input': ["btc", "domain", "email-src", "filename", "hostname", "ip-src", "ip-dst", "malware-type", "md5", "sha1", + "sha256", "url"], 'format': 'misp_standard'} moduleinfo = {'version': "0.1", 'author': "Jesse Hedden", 'description': "Enrich data with TruSTAR", 'module-type': ["hover", "expansion"]} -moduleconfig = ["api_key", "api_secret", "enclave_ids"] +moduleconfig = ["user_api_key", "user_api_secret", "enclave_ids"] -def get_results(misp_event): - event = json.loads(misp_event.to_json()) - results = {key: event[key] for key in ('Attribute', 'Object')} - return {'results': results} +class TruSTARParser: + ENTITY_TYPE_MAPPINGS = { + 'BITCOIN_ADDRESS': "btc", + 'CIDR_BLOCK': "ip-src", + 'CVE': "vulnerability", + 'URL': "url", + 'EMAIL_ADDRESS': "email-src", + 'SOFTWARE': "filename", + 'IP': "ip-src", + 'MALWARE': "malware-type", + 'MD5': "md5", + 'REGISTRY_KEY': "regkey", + 'SHA1': "sha1", + 'SHA256': "sha256" + } -def parse_indicator_summary(attribute, summary): - misp_event = MISPEvent() - misp_attribute = MISPAttribute().from_dict(**attribute) - misp_event.add_attribute(**misp_attribute) + REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" - mapping = {'value': 'text', 'reportId': 'text', 'enclaveId': 'text', 'description': 'text'} + def __init__(self, attribute, config): + config['enclave_ids'] = config.get('enclave_ids', "").split(',') + self.ts_client = TruStar(config=config) - for item in summary.get('items'): - trustar_obj = MISPObject(attribute.value) - for key, attribute_type in mapping.items(): - trustar_obj.add_attribute(key, attribute_type=attribute_type, value=item[key]) - trustar_obj.add_reference(misp_attribute.uuid, 'associated-to') - misp_event.add_object(**trustar_obj) + self.misp_event = MISPEvent() + self.misp_attribute = MISPAttribute() + self.misp_attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.misp_attribute) - return misp_event + def get_results(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + def generate_trustar_links(self, entity_value): + """ + Generates links to TruSTAR reports if they exist. -def handler(q=False): + :param entity_value: Value of entity. + """ + report_links = list() + trustar_reports = self.ts_client.search_reports(entity_value) + for report in trustar_reports: + report_links.append(self.REPORT_BASE_URL.format(report.id)) - if q is False: - return False + return report_links - request = json.loads(q) - config = request.get('config', {}) - if not config.get('api_key') or not config.get('api_secret'): - misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." - return misperrors + def parse_indicator_summary(self, attribute, summaries): - enclave_ids = [enclave_id for enclave_id in config.get('enclave_ids', "").split(',')] - ts_client = TruStar(config={'user_api_key': config.get('api_key'), 'user_api_secret': config.get('api_secret'), 'enclave_ids': enclave_ids}) - attribute = request.get('attribute') + for summary in summaries: + trustar_obj = MISPObject('trustar_report') + summary_dict = summary.to_dict() + summary_type = summary_dict.get('type') + summary_value = summary_dict.get('value') + if summary_type in self.ENTITY_TYPE_MAPPINGS: + trustar_obj.add_attribute(summary_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[summary_type], + value=summary_value) + trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", + value=json.dumps(summary_dict, sort_keys=True, indent=4)) + report_links = self.generate_trustar_links(summary_value) + for link in report_links: + trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=link) + self.misp_event.add_object(**trustar_obj) - summary = ts_client.get_indicator_summaries(attribute) + def handler(q=False): - misp_event = parse_indicator_summary(attribute, summary) - return get_results(misp_event) + if q is False: + return False -def introspection(): - return mispattributes + request = json.loads(q) -def version(): - moduleinfo['config'] = moduleconfig - return moduleinfo + config = request.get('config', {}) + if not config.get('user_api_key') or not config.get('user_api_secret'): + misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." + return misperrors + attribute = request['attribute'] + trustar_parser = TruSTARParser(attribute, config) + summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']]) + trustar_parser.parse_indicator_summary(attribute, summaries) + return trustar_parser.get_results() + + def introspection(): + return mispattributes + + def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 68b4fbba0960fc732180302ceecc4c726d7d2083 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 12:15:28 -0700 Subject: [PATCH 139/220] added client metatag to trustar client --- doc/expansion/trustar_enrich.json | 8 ++++++++ misp_modules/modules/expansion/trustar_enrich.py | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 doc/expansion/trustar_enrich.json diff --git a/doc/expansion/trustar_enrich.json b/doc/expansion/trustar_enrich.json new file mode 100644 index 0000000..d2f26bd --- /dev/null +++ b/doc/expansion/trustar_enrich.json @@ -0,0 +1,8 @@ +{ + "description": "Module to get information from ThreatMiner.", + "logo": "logos/threatminer.png", + "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512", + "output": "MISP attributes mapped from the result of the query on ThreatMiner, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- ssdeep\n- authentihash\n- filename\n- whois-registrant-email\n- url\n- link", + "references": ["https://www.threatminer.org/"], + "features": "This module takes a MISP attribute as input and queries ThreatMiner with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute." +} diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 38f5d16..db589fc 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -1,4 +1,5 @@ import json +import pymisp from pymisp import MISPAttribute, MISPEvent, MISPObject from trustar import TruStar @@ -32,8 +33,11 @@ class TruSTARParser: REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" + CLIENT_METATAG = "TruSTAR-MISP-{}".format(pymisp.__version__) + def __init__(self, attribute, config): config['enclave_ids'] = config.get('enclave_ids', "").split(',') + config['client_metatag'] = self.CLIENT_METATAG self.ts_client = TruStar(config=config) self.misp_event = MISPEvent() From 859bd19e24f7e2f5ad82cd4f514ba559322d7869 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 12:57:37 -0700 Subject: [PATCH 140/220] added module documentation --- doc/README.md | 29 +++++++++++++++++++++++++++++ doc/expansion/trustar_enrich.json | 12 ++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/doc/README.md b/doc/README.md index 37cb2c9..cb28526 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1168,6 +1168,35 @@ Module to get information from ThreatMiner. ----- +#### [trustar_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/trustar_enrich.py) + + + +Module to get enrich indicators with TruSTAR. +- **features**: +>This module enriches MISP attributes with scoring and metadata from TruSTAR. +> +>The TruSTAR indicator summary is appended to the attributes along with links to any associated reports. +- **input**: +>Any of the following MISP attributes: +>- btc +>- domain +>- email-src +>- filename +>- hostname +>- ip-src +>- ip-dst +>- md5 +>- sha1 +>- sha256 +>- url +- **output**: +>MISP attributes enriched with indicator summary data from the TruSTAR API. Data includes a severity level score and additional source and scoring info. +- **references**: +>https://docs.trustar.co/api/v13/indicators/get_indicator_summaries.html + +----- + #### [urlhaus](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/urlhaus.py) diff --git a/doc/expansion/trustar_enrich.json b/doc/expansion/trustar_enrich.json index d2f26bd..294419d 100644 --- a/doc/expansion/trustar_enrich.json +++ b/doc/expansion/trustar_enrich.json @@ -1,8 +1,8 @@ { - "description": "Module to get information from ThreatMiner.", - "logo": "logos/threatminer.png", - "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512", - "output": "MISP attributes mapped from the result of the query on ThreatMiner, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- ssdeep\n- authentihash\n- filename\n- whois-registrant-email\n- url\n- link", - "references": ["https://www.threatminer.org/"], - "features": "This module takes a MISP attribute as input and queries ThreatMiner with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute." + "description": "Module to get enrich indicators with TruSTAR.", + "logo": "logos/trustar.png", + "input": "Any of the following MISP attributes:\n- btc\n- domain\n- email-src\n- filename\n- hostname\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- url", + "output": "MISP attributes enriched with indicator summary data from the TruSTAR API. Data includes a severity level score and additional source and scoring info.", + "references": ["https://docs.trustar.co/api/v13/indicators/get_indicator_summaries.html"], + "features": "This module enriches MISP attributes with scoring and metadata from TruSTAR.\n\nThe TruSTAR indicator summary is appended to the attributes along with links to any associated reports." } From f3b27ca9c03d2fec4b55c4247e353f8a81721608 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 12:58:10 -0700 Subject: [PATCH 141/220] updated client metatag and version --- misp_modules/modules/expansion/trustar_enrich.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index db589fc..73854f3 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -33,11 +33,13 @@ class TruSTARParser: REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" - CLIENT_METATAG = "TruSTAR-MISP-{}".format(pymisp.__version__) + CLIENT_METATAG = "misp-v2" + CLIENT_VERSION = "{}".format(pymisp.__version__) def __init__(self, attribute, config): config['enclave_ids'] = config.get('enclave_ids', "").split(',') config['client_metatag'] = self.CLIENT_METATAG + config['client_version'] = self.CLIENT_VERSION self.ts_client = TruStar(config=config) self.misp_event = MISPEvent() From 8e8c580a83bb64e230338a2275ed892aa7d2cef9 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 12:58:32 -0700 Subject: [PATCH 142/220] uploaded TruSTAR logo --- doc/logos/trustar.png | Bin 0 -> 37780 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/logos/trustar.png diff --git a/doc/logos/trustar.png b/doc/logos/trustar.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ac52138cbbab144bf22732e0ebdbc81d48be39 GIT binary patch literal 37780 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 z z(e67(>fQO=lRw@r@(9<*%MsTlPJpMV4I?qoaWI{pro^Dne$%pShRN@Ye_K%g@L2zzH5_-=Uuw7qOL z_2ehf`*teqlyJ;B=~R=HeY(ZnF7#(wTok&;lsYpt1e$s#56ZqIplYn38k@w4to}>pXys^r5+S7b{PEO8V%q=%jg~C+D2|(Bt zM~Vc^PT=|HpD&3-B2y?tKZTyvPxeQh*I#In((2n>;zFBKTxfNO`ZhPF*r{C6JoT(F zBP{jHiiv(1QBjx?5&g2|K)N&fB|RJG(E4_l%eDQn#~wSWvMER&1WZK$!fvYSg0XrK z;9h-GQ`2Y)-;XCTsUX20AB)N38w1Xvbv_b$5_|rv^SOAc9hrz{_KDS#B-6-rQIr{x ziZY|^BF>+AY0(;oL)uHs_Y(7c6jI-to11$Oz4658;%fU5fUw)I&QW582&`VcI;W+j zrGk2o<5}z}*$P@6ui|2xBd00o6wM^^O#w+X)AuI&PM@}Ye$UUrbG%V0mjs+XMQ)lb za_D<5eb3K`HPfnY`-tB@7IBJ_?9I;3-Zy*p>}G|k3dw+gtq4HaZB^YUtQG`lPW=E9 zr(q;g!%6Um(R(=MJB;4L+d`t0MBd9{-yC#^M!!SQNPXdt&^s*gryPoyYw(`OCFFEh zOiFjlf<6U}Ko@!GEZ`j?9t&pB0)bTiWAr{wT#nKEIB_{f{}dgkx#Q>PI9?zC0tyj; zuqy--$$&sS1W3%JB}r?<1nE0nC!#cIi&xW)L@#~vZ`vJ?j!Sgh!JFpLb(gNCJOpiKLT|bK(;>wqTB}9S z?qtGAo`4={3zzfKC;#Say)Id#kyx{6r_uKg@xJss7D;!D2D+|}Y_BKV>nIC;vxv6~ zH-9czu;4VE!}KzOffop9K>)(81!d#`0tph}e*j*1;f35#C{#$dvgj95@3xSh^B3_a z9m}O-9(sF8G(Gg2#)``x{@%^8_H%3koe$CZAb--&Abk(f_m0o2j`4Xq*G$oedV0QH z$Dj1Oo{rafJf8X={pd$c^c#XrK=1+qBN6!jVFqddJlry&00000NkvXXu0mjf{(_nZ literal 0 HcmV?d00001 From f13233d04c74b2e4644be2edf2a278edf13122ae Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 13:47:25 -0700 Subject: [PATCH 143/220] added comments and increased page size to max for get_indicator_summaries --- misp_modules/modules/expansion/trustar_enrich.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 73854f3..4e8d916 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -48,6 +48,9 @@ class TruSTARParser: self.misp_event.add_attribute(**self.misp_attribute) def get_results(self): + """ + Returns the MISP Event enriched with TruSTAR indicator summary data. + """ event = json.loads(self.misp_event.to_json()) results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} return {'results': results} @@ -65,7 +68,14 @@ class TruSTARParser: return report_links - def parse_indicator_summary(self, attribute, summaries): + def parse_indicator_summary(self, summaries): + """ + Converts a response from the TruSTAR /1.3/indicators/summaries endpoint + a MISP trustar_report object and adds the summary data and links as attributes. + + :param summaries: A TruSTAR Python SDK Page.generator object for generating + indicator summaries pages. + """ for summary in summaries: trustar_obj = MISPObject('trustar_report') @@ -96,7 +106,7 @@ class TruSTARParser: attribute = request['attribute'] trustar_parser = TruSTARParser(attribute, config) - summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']]) + summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=100) trustar_parser.parse_indicator_summary(attribute, summaries) return trustar_parser.get_results() From b9d191686f5f0d2b802aca7f2d8a6022dfe7f37b Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 14:54:37 -0700 Subject: [PATCH 144/220] added try/except for TruSTAR API errors and additional comments --- misp_modules/modules/expansion/trustar_enrich.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 4e8d916..1edc0f8 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -14,6 +14,8 @@ moduleinfo = {'version': "0.1", 'author': "Jesse Hedden", moduleconfig = ["user_api_key", "user_api_secret", "enclave_ids"] +MAX_PAGE_SIZE = 100 # Max allowable page size returned from /1.3/indicators/summaries endpoint + class TruSTARParser: ENTITY_TYPE_MAPPINGS = { @@ -93,6 +95,12 @@ class TruSTARParser: self.misp_event.add_object(**trustar_obj) def handler(q=False): + """ + MISP handler function. A user's API key and secret will be retrieved from the MISP + request and used to create a TruSTAR API client. If enclave IDs are provided, only + those enclaves will be queried for data. Otherwise, all of the enclaves a user has + access to will be queried. + """ if q is False: return False @@ -106,7 +114,13 @@ class TruSTARParser: attribute = request['attribute'] trustar_parser = TruSTARParser(attribute, config) - summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=100) + + try: + summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE) + except Exception as e: + misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) + return misperrors + trustar_parser.parse_indicator_summary(attribute, summaries) return trustar_parser.get_results() From b60d142d32fcd3ca5c8e90256469005a67d383f1 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 22 Jun 2020 15:06:39 -0700 Subject: [PATCH 145/220] removed extra parameter --- misp_modules/modules/expansion/trustar_enrich.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 1edc0f8..f163b85 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -121,7 +121,7 @@ class TruSTARParser: misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) return misperrors - trustar_parser.parse_indicator_summary(attribute, summaries) + trustar_parser.parse_indicator_summary(summaries) return trustar_parser.get_results() def introspection(): From b188d2da4e3f5c09755811aab567989c6a8567e8 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Wed, 24 Jun 2020 17:47:41 -0700 Subject: [PATCH 146/220] added strip to remove potential whitespace --- misp_modules/modules/expansion/trustar_enrich.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index f163b85..974a3f5 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -39,7 +39,7 @@ class TruSTARParser: CLIENT_VERSION = "{}".format(pymisp.__version__) def __init__(self, attribute, config): - config['enclave_ids'] = config.get('enclave_ids', "").split(',') + config['enclave_ids'] = config.get('enclave_ids', "").strip().split(',') config['client_metatag'] = self.CLIENT_METATAG config['client_version'] = self.CLIENT_VERSION self.ts_client = TruStar(config=config) From 61fbb30e1c975f2487fc22b24b1eb1d979ad553b Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Thu, 25 Jun 2020 10:54:34 -0700 Subject: [PATCH 147/220] fixed metatag; convert summaries generator to list for error handling --- misp_modules/modules/expansion/trustar_enrich.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 974a3f5..ca5c886 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -14,7 +14,7 @@ moduleinfo = {'version': "0.1", 'author': "Jesse Hedden", moduleconfig = ["user_api_key", "user_api_secret", "enclave_ids"] -MAX_PAGE_SIZE = 100 # Max allowable page size returned from /1.3/indicators/summaries endpoint +MAX_PAGE_SIZE = 100 # Max allowable page size returned from /1.3/indicators/summaries endpoint class TruSTARParser: @@ -35,13 +35,11 @@ class TruSTARParser: REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" - CLIENT_METATAG = "misp-v2" - CLIENT_VERSION = "{}".format(pymisp.__version__) + CLIENT_METATAG = "MISP-{}".format(pymisp.__version__) def __init__(self, attribute, config): config['enclave_ids'] = config.get('enclave_ids', "").strip().split(',') config['client_metatag'] = self.CLIENT_METATAG - config['client_version'] = self.CLIENT_VERSION self.ts_client = TruStar(config=config) self.misp_event = MISPEvent() @@ -81,14 +79,13 @@ class TruSTARParser: for summary in summaries: trustar_obj = MISPObject('trustar_report') - summary_dict = summary.to_dict() - summary_type = summary_dict.get('type') - summary_value = summary_dict.get('value') + summary_type = summary.type + summary_value = summary.value if summary_type in self.ENTITY_TYPE_MAPPINGS: trustar_obj.add_attribute(summary_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[summary_type], value=summary_value) trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", - value=json.dumps(summary_dict, sort_keys=True, indent=4)) + value=json.dumps(summary.to_dict(), sort_keys=True, indent=4)) report_links = self.generate_trustar_links(summary_value) for link in report_links: trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=link) @@ -116,7 +113,8 @@ class TruSTARParser: trustar_parser = TruSTARParser(attribute, config) try: - summaries = trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE) + summaries = list( + trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE)) except Exception as e: misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) return misperrors From 2d31b4e037ee2b496ec23a8c328632f0975873c5 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Thu, 25 Jun 2020 13:10:50 -0700 Subject: [PATCH 148/220] fixed incorrect attribute name --- misp_modules/modules/expansion/trustar_enrich.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index ca5c886..50b3d55 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -79,14 +79,14 @@ class TruSTARParser: for summary in summaries: trustar_obj = MISPObject('trustar_report') - summary_type = summary.type - summary_value = summary.value + indicator_type = summary.indicator_type + indicator_value = summary.value if summary_type in self.ENTITY_TYPE_MAPPINGS: - trustar_obj.add_attribute(summary_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[summary_type], - value=summary_value) + trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], + value=indicator_value) trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", value=json.dumps(summary.to_dict(), sort_keys=True, indent=4)) - report_links = self.generate_trustar_links(summary_value) + report_links = self.generate_trustar_links(indicator_value) for link in report_links: trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=link) self.misp_event.add_object(**trustar_obj) From 9e1bc5681b0cbdf28ab8d9f9b88e0e7d828c6cfc Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Thu, 25 Jun 2020 15:22:54 -0700 Subject: [PATCH 149/220] fixed indent --- .../modules/expansion/trustar_enrich.py | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 50b3d55..48b4895 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -91,40 +91,43 @@ class TruSTARParser: trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=link) self.misp_event.add_object(**trustar_obj) - def handler(q=False): - """ - MISP handler function. A user's API key and secret will be retrieved from the MISP - request and used to create a TruSTAR API client. If enclave IDs are provided, only - those enclaves will be queried for data. Otherwise, all of the enclaves a user has - access to will be queried. - """ - if q is False: - return False +def handler(q=False): + """ + MISP handler function. A user's API key and secret will be retrieved from the MISP + request and used to create a TruSTAR API client. If enclave IDs are provided, only + those enclaves will be queried for data. Otherwise, all of the enclaves a user has + access to will be queried. + """ - request = json.loads(q) + if q is False: + return False - config = request.get('config', {}) - if not config.get('user_api_key') or not config.get('user_api_secret'): - misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." - return misperrors + request = json.loads(q) - attribute = request['attribute'] - trustar_parser = TruSTARParser(attribute, config) + config = request.get('config', {}) + if not config.get('user_api_key') or not config.get('user_api_secret'): + misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." + return misperrors - try: - summaries = list( - trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE)) - except Exception as e: - misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) - return misperrors + attribute = request['attribute'] + trustar_parser = TruSTARParser(attribute, config) - trustar_parser.parse_indicator_summary(summaries) - return trustar_parser.get_results() + try: + summaries = list( + trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE)) + except Exception as e: + misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) + return misperrors - def introspection(): - return mispattributes + trustar_parser.parse_indicator_summary(summaries) + return trustar_parser.get_results() - def version(): - moduleinfo['config'] = moduleconfig - return moduleinfo + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From a91d50b5073ec8890d3eec1c704d6e4626584bc9 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sat, 27 Jun 2020 17:29:01 -0700 Subject: [PATCH 150/220] corrected variable name --- misp_modules/modules/expansion/trustar_enrich.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 48b4895..efe7c53 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -81,7 +81,7 @@ class TruSTARParser: trustar_obj = MISPObject('trustar_report') indicator_type = summary.indicator_type indicator_value = summary.value - if summary_type in self.ENTITY_TYPE_MAPPINGS: + if indicator_type in self.ENTITY_TYPE_MAPPINGS: trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], value=indicator_value) trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", From a70558945a1124c70875a5d9f217a55439828ea9 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sat, 27 Jun 2020 17:46:51 -0700 Subject: [PATCH 151/220] removed obsolete file --- misp_modules/modules/import_mod/trustar_import.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 misp_modules/modules/import_mod/trustar_import.py diff --git a/misp_modules/modules/import_mod/trustar_import.py b/misp_modules/modules/import_mod/trustar_import.py deleted file mode 100644 index 2c55be2..0000000 --- a/misp_modules/modules/import_mod/trustar_import.py +++ /dev/null @@ -1,7 +0,0 @@ -import base64 -import json - -from trustar import TruStar - -misp_errors = {'error': "Error"} - From c0dae2b31b309557acaaeb4f0ccbd887e6b01780 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 30 Jun 2020 18:08:34 +0200 Subject: [PATCH 152/220] fix: Removed trustar_import module name in init to avoid validation issues (until it is submitted via PR?) --- misp_modules/modules/import_mod/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misp_modules/modules/import_mod/__init__.py b/misp_modules/modules/import_mod/__init__.py index 45e3359..fbad911 100644 --- a/misp_modules/modules/import_mod/__init__.py +++ b/misp_modules/modules/import_mod/__init__.py @@ -15,5 +15,4 @@ __all__ = [ 'threatanalyzer_import', 'csvimport', 'joe_import', - 'trustar_import', ] From de8d78cc70e66413ee0c425782579e56dee53a25 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 30 Jun 2020 18:41:42 +0200 Subject: [PATCH 153/220] add: Trustar python library added to Pipfile --- Pipfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Pipfile b/Pipfile index 9e651de..8459176 100644 --- a/Pipfile +++ b/Pipfile @@ -60,6 +60,7 @@ geoip2 = "*" apiosintDS = "*" assemblyline_client = "*" vt-graph-api = "*" +trustar = "*" [requires] python_version = "3" From f45d9964f3e65abdc7a2f216bf10ef61aa6db7be Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Tue, 30 Jun 2020 10:07:16 -0700 Subject: [PATCH 154/220] removed obsoleted module name --- misp_modules/modules/import_mod/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misp_modules/modules/import_mod/__init__.py b/misp_modules/modules/import_mod/__init__.py index 45e3359..fbad911 100644 --- a/misp_modules/modules/import_mod/__init__.py +++ b/misp_modules/modules/import_mod/__init__.py @@ -15,5 +15,4 @@ __all__ = [ 'threatanalyzer_import', 'csvimport', 'joe_import', - 'trustar_import', ] From 26b0357ac71c66b4b5f7065827e9d4e461fcfedc Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 30 Jun 2020 23:10:35 +0200 Subject: [PATCH 155/220] fix: Making pep8 happy --- misp_modules/modules/expansion/rbl.py | 10 +++++----- misp_modules/modules/import_mod/csvimport.py | 4 ++-- .../modules/import_mod/threatanalyzer_import.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/misp_modules/modules/expansion/rbl.py b/misp_modules/modules/expansion/rbl.py index 73f1b9b..4d7bba5 100644 --- a/misp_modules/modules/expansion/rbl.py +++ b/misp_modules/modules/expansion/rbl.py @@ -88,18 +88,18 @@ def handler(q=False): else: misperrors['error'] = "Unsupported attributes type" return misperrors - listed = [] - info = [] + listeds = [] + infos = [] ipRev = '.'.join(ip.split('.')[::-1]) for rbl in rbls: query = '{}.{}'.format(ipRev, rbl) try: txt = resolver.query(query, 'TXT') - listed.append(query) - info.append([str(t) for t in txt]) + listeds.append(query) + infos.append([str(t) for t in txt]) except Exception: continue - result = "\n".join(["{}: {}".format(l, " - ".join(i)) for l, i in zip(listed, info)]) + result = "\n".join([f"{listed}: {' - '.join(info)}" for listed, info in zip(listeds, infos)]) if not result: return {'error': 'No data found by querying known RBLs'} return {'results': [{'types': mispattributes.get('output'), 'values': result}]} diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index e4dc2e5..3ea7c25 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -244,11 +244,11 @@ def __any_mandatory_misp_field(header): def __special_parsing(data, delimiter): - return list(tuple(l.strip() for l in line[0].split(delimiter)) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) + return list(tuple(part.strip() for part in line[0].split(delimiter)) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) def __standard_parsing(data): - return list(tuple(l.strip() for l in line) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) + return list(tuple(part.strip() for part in line) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) def handler(q=False): diff --git a/misp_modules/modules/import_mod/threatanalyzer_import.py b/misp_modules/modules/import_mod/threatanalyzer_import.py index ff0a5b1..cbb9fef 100755 --- a/misp_modules/modules/import_mod/threatanalyzer_import.py +++ b/misp_modules/modules/import_mod/threatanalyzer_import.py @@ -99,7 +99,7 @@ def handler(q=False): results = process_analysis_json(json.loads(data.decode('utf-8'))) except ValueError: log.warning('MISP modules {0} failed: uploaded file is not a zip or json file.'.format(request['module'])) - return {'error': 'Uploaded file is not a zip or json file.'.format(request['module'])} + return {'error': 'Uploaded file is not a zip or json file.'} pass # keep only unique entries based on the value field results = list({v['values']: v for v in results}.values()) From f99174af2e6f571e262af4c9ea54b9357071391c Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 1 Jul 2020 11:27:36 +0200 Subject: [PATCH 156/220] fix: Removed multiple spaces to comply with pep8 --- misp_modules/modules/import_mod/csvimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/import_mod/csvimport.py b/misp_modules/modules/import_mod/csvimport.py index 3ea7c25..34eed8c 100644 --- a/misp_modules/modules/import_mod/csvimport.py +++ b/misp_modules/modules/import_mod/csvimport.py @@ -244,7 +244,7 @@ def __any_mandatory_misp_field(header): def __special_parsing(data, delimiter): - return list(tuple(part.strip() for part in line[0].split(delimiter)) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) + return list(tuple(part.strip() for part in line[0].split(delimiter)) for line in csv.reader(io.TextIOWrapper(io.BytesIO(data.encode()), encoding='utf-8')) if line and not line[0].startswith('#')) def __standard_parsing(data): From cda5feedaadc4f06eb7a4c83347ee504feef29e8 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Wed, 1 Jul 2020 16:13:40 +0200 Subject: [PATCH 157/220] fix: [virustotal] Subdomains is optional in VT response --- misp_modules/modules/expansion/virustotal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index f47a2e3..b09de81 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -52,7 +52,7 @@ class VirusTotalParser(object): 'downloaded': 'downloaded-from', 'referrer': 'referring'} siblings = (self.parse_siblings(domain) for domain in req['domain_siblings']) - uuid = self.parse_resolutions(req['resolutions'], req['subdomains'], siblings) + uuid = self.parse_resolutions(req['resolutions'], req['subdomains'] if 'subdomains' in req else None, siblings) for feature_type, relationship in feature_types.items(): for feature in ('undetected_{}_samples', 'detected_{}_samples'): for sample in req.get(feature.format(feature_type), [])[:self.limit]: From b5e0995926d9064d0d3e8f5195e241208c0d027b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 3 Jul 2020 09:41:20 +0200 Subject: [PATCH 158/220] fix: Fixed validators dependency issues - Possible rollback if we get issues with virustotal --- Pipfile | 2 +- Pipfile.lock | 734 +++++++++++++++++++++++++++------------------------ 2 files changed, 395 insertions(+), 341 deletions(-) diff --git a/Pipfile b/Pipfile index 8459176..1169368 100644 --- a/Pipfile +++ b/Pipfile @@ -18,7 +18,7 @@ pypdns = "*" pypssl = "*" pyeupi = "*" uwhois = {editable = true,git = "https://github.com/Rafiot/uwhoisd.git",ref = "testing",subdirectory = "client"} -pymisp = {editable = true,extras = ["fileobjects,openioc,virustotal,pdfexport"],git = "https://github.com/MISP/PyMISP.git"} +pymisp = {editable = true,extras = ["fileobjects,openioc,pdfexport"],git = "https://github.com/MISP/PyMISP.git"} pyonyphe = {editable = true,git = "https://github.com/sebdraven/pyonyphe"} pydnstrails = {editable = true,git = "https://github.com/sebdraven/pydnstrails"} pytesseract = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 9a23d0d..73aeaed 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b62db6df8a7b42f4c6915d6fbb1d4c38ccbb7209e559708433d28cdddebd3df9" + "sha256": "c2d937b384431e4b313b29bb02db0bd1d3a866ddcb7c6e91cbfa34f88d351b59" }, "pipfile-spec": 6, "requires": { @@ -18,30 +18,21 @@ "default": { "aiohttp": { "hashes": [ - "sha256:0419705a36b43c0ac6f15469f9c2a08cad5c939d78bd12a5c23ea167c8253b2b", - "sha256:1812fc4bc6ac1bde007daa05d2d0f61199324e0cc893b11523e646595047ca08", - "sha256:2214b5c0153f45256d5d52d1e0cafe53f9905ed035a142191727a5fb620c03dd", - "sha256:275909137f0c92c61ba6bb1af856a522d5546f1de8ea01e4e726321c697754ac", - "sha256:3983611922b561868428ea1e7269e757803713f55b53502423decc509fef1650", - "sha256:51afec6ffa50a9da4cdef188971a802beb1ca8e8edb40fa429e5e529db3475fa", - "sha256:589f2ec8a101a0f340453ee6945bdfea8e1cd84c8d88e5be08716c34c0799d95", - "sha256:789820ddc65e1f5e71516adaca2e9022498fa5a837c79ba9c692a9f8f916c330", - "sha256:7a968a0bdaaf9abacc260911775611c9a602214a23aeb846f2eb2eeaa350c4dc", - "sha256:7aeefbed253f59ea39e70c5848de42ed85cb941165357fc7e87ab5d8f1f9592b", - "sha256:7b2eb55c66512405103485bd7d285a839d53e7fdc261ab20e5bcc51d7aaff5de", - "sha256:87bc95d3d333bb689c8d755b4a9d7095a2356108002149523dfc8e607d5d32a4", - "sha256:9d80e40db208e29168d3723d1440ecbb06054d349c5ece6a2c5a611490830dd7", - "sha256:a1b442195c2a77d33e4dbee67c9877ccbdd3a1f686f91eb479a9577ed8cc326b", - "sha256:ab3d769413b322d6092f169f316f7b21cd261a7589f7e31db779d5731b0480d8", - "sha256:b066d3dec5d0f5aee6e34e5765095dc3d6d78ef9839640141a2b20816a0642bd", - "sha256:b24e7845ae8de3e388ef4bcfcf7f96b05f52c8e633b33cf8003a6b1d726fc7c2", - "sha256:c59a953c3f8524a7c86eaeaef5bf702555be12f5668f6384149fe4bb75c52698", - "sha256:cf2cc6c2c10d242790412bea7ccf73726a9a44b4c4b073d2699ef3b48971fd95", - "sha256:e0c9c8d4150ae904f308ff27b35446990d2b1dfc944702a21925937e937394c6", - "sha256:f1839db4c2b08a9c8f9788112644f8a8557e8e0ecc77b07091afabb941dc55d0", - "sha256:f3df52362be39908f9c028a65490fae0475e4898b43a03d8aa29d1e765b45e07" + "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", + "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", + "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", + "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", + "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", + "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", + "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", + "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", + "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", + "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", + "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", + "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" ], - "version": "==3.4.4" + "markers": "python_full_version >= '3.5.3'", + "version": "==3.6.2" }, "antlr4-python3-runtime": { "hashes": [ @@ -77,6 +68,7 @@ "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" ], + "markers": "python_full_version >= '3.5.3'", "version": "==3.0.1" }, "attrs": { @@ -84,6 +76,7 @@ "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==19.3.0" }, "backscatter": { @@ -96,12 +89,12 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", - "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", - "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae" + "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7", + "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8", + "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c" ], "index": "pypi", - "version": "==4.8.2" + "version": "==4.9.1" }, "blockchain": { "hashes": [ @@ -112,10 +105,10 @@ }, "certifi": { "hashes": [ - "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", - "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" ], - "version": "==2020.4.5.1" + "version": "==2020.6.20" }, "cffi": { "hashes": [ @@ -162,6 +155,7 @@ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==7.1.2" }, "click-plugins": { @@ -176,8 +170,17 @@ "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.4.3" }, + "configparser": { + "hashes": [ + "sha256:2ca44140ee259b5e3d8aaf47c79c36a7ab0d5e94d70bd4105c03ede7a20ea5a1", + "sha256:cffc044844040c7ce04e9acd1838b5f2e5fa3170182f6fda4d2ea8b0099dbadd" + ], + "markers": "python_version >= '3.6'", + "version": "==5.0.0" + }, "cryptography": { "hashes": [ "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", @@ -214,6 +217,7 @@ "sha256:525ba66fb5f90b07169fdd48b6373c18f1ee12728ca277ca44567a367d9d7f74", "sha256:a766c1dccb30c5f6eb2b203f87edd1d8588847709c78589e1521d769addc8218" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.2.10" }, "dnspython": { @@ -226,10 +230,11 @@ }, "domaintools-api": { "hashes": [ - "sha256:f567f407b8997e947df5badf7c2bea64fdfd33c54ade24eab36ef575fb71ccb7" + "sha256:62e2e688d14dbd7ca51a44bd0a8490aa69c712895475598afbdbb1e1e15bf2f2", + "sha256:fe75e3cc86e7e2904b06d8e94b1986e721fdce85d695c87d1140403957e4c989" ], "index": "pypi", - "version": "==0.3.3" + "version": "==0.5.2" }, "enum-compat": { "hashes": [ @@ -255,6 +260,7 @@ "hashes": [ "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.18.2" }, "futures": { @@ -275,24 +281,23 @@ }, "httplib2": { "hashes": [ - "sha256:4f6988e6399a2546b525a037d56da34aed4d149bbdc0e78523018d5606c26e74", - "sha256:b0e1f3ed76c97380fe2485bc47f25235453b40ef33ca5921bb2897e257a49c4c" + "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3", + "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782" ], - "index": "pypi", - "version": "==0.18.0" + "version": "==0.18.1" }, "idna": { "hashes": [ - "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", - "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], - "version": "==2.9" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" }, "idna-ssl": { "hashes": [ "sha256:a933e3bb13da54383f9e8f35dc4f9cb9eb9b3b78c6b36f311254d6d0d92c6c7c" ], - "index": "pypi", "markers": "python_version < '3.7'", "version": "==1.1.0" }, @@ -305,10 +310,10 @@ }, "jbxapi": { "hashes": [ - "sha256:98253ba0bf79a9d0c87d823d54e2f7568625708185b3d4517ee4982cc964d888" + "sha256:58eb7d77a52169309e2322ce874c0f00a7900a515d1d0798ff85973cdb2766e3" ], "index": "pypi", - "version": "==3.4.0" + "version": "==3.8.0" }, "jsonschema": { "hashes": [ @@ -338,36 +343,36 @@ }, "lxml": { "hashes": [ - "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd", - "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c", - "sha256:1f2c4ec372bf1c4a2c7e4bb20845e8bcf8050365189d86806bad1e3ae473d081", - "sha256:4235bc124fdcf611d02047d7034164897ade13046bda967768836629bc62784f", - "sha256:5828c7f3e615f3975d48f40d4fe66e8a7b25f16b5e5705ffe1d22e43fb1f6261", - "sha256:585c0869f75577ac7a8ff38d08f7aac9033da2c41c11352ebf86a04652758b7a", - "sha256:5d467ce9c5d35b3bcc7172c06320dddb275fea6ac2037f72f0a4d7472035cea9", - "sha256:63dbc21efd7e822c11d5ddbedbbb08cd11a41e0032e382a0fd59b0b08e405a3a", - "sha256:7bc1b221e7867f2e7ff1933165c0cec7153dce93d0cdba6554b42a8beb687bdb", - "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60", - "sha256:8a0ebda56ebca1a83eb2d1ac266649b80af8dd4b4a3502b2c1e09ac2f88fe128", - "sha256:90ed0e36455a81b25b7034038e40880189169c308a3df360861ad74da7b68c1a", - "sha256:95e67224815ef86924fbc2b71a9dbd1f7262384bca4bc4793645794ac4200717", - "sha256:afdb34b715daf814d1abea0317b6d672476b498472f1e5aacbadc34ebbc26e89", - "sha256:b4b2c63cc7963aedd08a5f5a454c9f67251b1ac9e22fd9d72836206c42dc2a72", - "sha256:d068f55bda3c2c3fcaec24bd083d9e2eede32c583faf084d6e4b9daaea77dde8", - "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3", - "sha256:deadf4df349d1dcd7b2853a2c8796593cc346600726eff680ed8ed11812382a7", - "sha256:df533af6f88080419c5a604d0d63b2c33b1c0c4409aba7d0cb6de305147ea8c8", - "sha256:e4aa948eb15018a657702fee0b9db47e908491c64d36b4a90f59a64741516e77", - "sha256:e5d842c73e4ef6ed8c1bd77806bf84a7cb535f9c0cf9b2c74d02ebda310070e1", - "sha256:ebec08091a22c2be870890913bdadd86fcd8e9f0f22bcb398abd3af914690c15", - "sha256:edc15fcfd77395e24543be48871c251f38132bb834d9fdfdad756adb6ea37679", - "sha256:f2b74784ed7e0bc2d02bd53e48ad6ba523c9b36c194260b7a5045071abbb1012", - "sha256:fa071559f14bd1e92077b1b5f6c22cf09756c6de7139370249eb372854ce51e6", - "sha256:fd52e796fee7171c4361d441796b64df1acfceb51f29e545e812f16d023c4bbc", - "sha256:fe976a0f1ef09b3638778024ab9fb8cde3118f203364212c198f71341c0715ca" + "sha256:06748c7192eab0f48e3d35a7adae609a329c6257495d5e53878003660dc0fec6", + "sha256:0790ddca3f825dd914978c94c2545dbea5f56f008b050e835403714babe62a5f", + "sha256:1aa7a6197c1cdd65d974f3e4953764eee3d9c7b67e3966616b41fab7f8f516b7", + "sha256:22c6d34fdb0e65d5f782a4d1a1edb52e0a8365858dafb1c08cb1d16546cf0786", + "sha256:2754d4406438c83144f9ffd3628bbe2dcc6d62b20dbc5c1ec4bc4385e5d44b42", + "sha256:27ee0faf8077c7c1a589573b1450743011117f1aa1a91d5ae776bbc5ca6070f2", + "sha256:2b02c106709466a93ed424454ce4c970791c486d5fcdf52b0d822a7e29789626", + "sha256:2d1ddce96cf15f1254a68dba6935e6e0f1fe39247de631c115e84dd404a6f031", + "sha256:4f282737d187ae723b2633856085c31ae5d4d432968b7f3f478a48a54835f5c4", + "sha256:51bb4edeb36d24ec97eb3e6a6007be128b720114f9a875d6b370317d62ac80b9", + "sha256:7eee37c1b9815e6505847aa5e68f192e8a1b730c5c7ead39ff317fde9ce29448", + "sha256:7fd88cb91a470b383aafad554c3fe1ccf6dfb2456ff0e84b95335d582a799804", + "sha256:9144ce36ca0824b29ebc2e02ca186e54040ebb224292072250467190fb613b96", + "sha256:925baf6ff1ef2c45169f548cc85204433e061360bfa7d01e1be7ae38bef73194", + "sha256:a636346c6c0e1092ffc202d97ec1843a75937d8c98aaf6771348ad6422e44bb0", + "sha256:a87dbee7ad9dce3aaefada2081843caf08a44a8f52e03e0a4cc5819f8398f2f4", + "sha256:a9e3b8011388e7e373565daa5e92f6c9cb844790dc18e43073212bb3e76f7007", + "sha256:afb53edf1046599991fb4a7d03e601ab5f5422a5435c47ee6ba91ec3b61416a6", + "sha256:b26719890c79a1dae7d53acac5f089d66fd8cc68a81f4e4bd355e45470dc25e1", + "sha256:b7462cdab6fffcda853338e1741ce99706cdf880d921b5a769202ea7b94e8528", + "sha256:b77975465234ff49fdad871c08aa747aae06f5e5be62866595057c43f8d2f62c", + "sha256:c47a8a5d00060122ca5908909478abce7bbf62d812e3fc35c6c802df8fb01fe7", + "sha256:c79e5debbe092e3c93ca4aee44c9a7631bdd407b2871cb541b979fd350bbbc29", + "sha256:d8d40e0121ca1606aa9e78c28a3a7d88a05c06b3ca61630242cded87d8ce55fa", + "sha256:ee2be8b8f72a2772e72ab926a3bccebf47bb727bda41ae070dc91d1fb759b726", + "sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9", + "sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529" ], "index": "pypi", - "version": "==4.5.0" + "version": "==4.5.1" }, "maclookup": { "hashes": [ @@ -381,6 +386,7 @@ "hashes": [ "sha256:f4d28823d9ca23323d113dc7af8db2087aa4f657fafc64ff8f7a8afda871425b" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==1.5.4" }, "misp-modules": { @@ -407,6 +413,7 @@ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" ], + "markers": "python_version >= '3.5'", "version": "==4.7.6" }, "np": { @@ -418,29 +425,35 @@ }, "numpy": { "hashes": [ - "sha256:00d7b54c025601e28f468953d065b9b121ddca7fff30bed7be082d3656dd798d", - "sha256:02ec9582808c4e48be4e93cd629c855e644882faf704bc2bd6bbf58c08a2a897", - "sha256:0e6f72f7bb08f2f350ed4408bb7acdc0daba637e73bce9f5ea2b207039f3af88", - "sha256:1be2e96314a66f5f1ce7764274327fd4fb9da58584eaff00b5a5221edefee7d6", - "sha256:2466fbcf23711ebc5daa61d28ced319a6159b260a18839993d871096d66b93f7", - "sha256:2b573fcf6f9863ce746e4ad00ac18a948978bb3781cffa4305134d31801f3e26", - "sha256:3f0dae97e1126f529ebb66f3c63514a0f72a177b90d56e4bce8a0b5def34627a", - "sha256:50fb72bcbc2cf11e066579cb53c4ca8ac0227abb512b6cbc1faa02d1595a2a5d", - "sha256:57aea170fb23b1fd54fa537359d90d383d9bf5937ee54ae8045a723caa5e0961", - "sha256:709c2999b6bd36cdaf85cf888d8512da7433529f14a3689d6e37ab5242e7add5", - "sha256:7d59f21e43bbfd9a10953a7e26b35b6849d888fc5a331fa84a2d9c37bd9fe2a2", - "sha256:904b513ab8fbcbdb062bed1ce2f794ab20208a1b01ce9bd90776c6c7e7257032", - "sha256:96dd36f5cdde152fd6977d1bbc0f0561bccffecfde63cd397c8e6033eb66baba", - "sha256:9933b81fecbe935e6a7dc89cbd2b99fea1bf362f2790daf9422a7bb1dc3c3085", - "sha256:bbcc85aaf4cd84ba057decaead058f43191cc0e30d6bc5d44fe336dc3d3f4509", - "sha256:dccd380d8e025c867ddcb2f84b439722cf1f23f3a319381eac45fd077dee7170", - "sha256:e22cd0f72fc931d6abc69dc7764484ee20c6a60b0d0fee9ce0426029b1c1bdae", - "sha256:ed722aefb0ebffd10b32e67f48e8ac4c5c4cf5d3a785024fdf0e9eb17529cd9d", - "sha256:efb7ac5572c9a57159cf92c508aad9f856f1cb8e8302d7fdb99061dbe52d712c", - "sha256:efdba339fffb0e80fcc19524e4fdbda2e2b5772ea46720c44eaac28096d60720", - "sha256:f22273dd6a403ed870207b853a856ff6327d5cbce7a835dfa0645b3fc00273ec" + "sha256:13af0184177469192d80db9bd02619f6fa8b922f9f327e077d6f2a6acb1ce1c0", + "sha256:26a45798ca2a4e168d00de75d4a524abf5907949231512f372b217ede3429e98", + "sha256:26f509450db547e4dfa3ec739419b31edad646d21fb8d0ed0734188b35ff6b27", + "sha256:30a59fb41bb6b8c465ab50d60a1b298d1cd7b85274e71f38af5a75d6c475d2d2", + "sha256:33c623ef9ca5e19e05991f127c1be5aeb1ab5cdf30cb1c5cf3960752e58b599b", + "sha256:356f96c9fbec59974a592452ab6a036cd6f180822a60b529a975c9467fcd5f23", + "sha256:3c40c827d36c6d1c3cf413694d7dc843d50997ebffbc7c87d888a203ed6403a7", + "sha256:4d054f013a1983551254e2379385e359884e5af105e3efe00418977d02f634a7", + "sha256:63d971bb211ad3ca37b2adecdd5365f40f3b741a455beecba70fd0dde8b2a4cb", + "sha256:658624a11f6e1c252b2cd170d94bf28c8f9410acab9f2fd4369e11e1cd4e1aaf", + "sha256:76766cc80d6128750075378d3bb7812cf146415bd29b588616f72c943c00d598", + "sha256:7b57f26e5e6ee2f14f960db46bd58ffdca25ca06dd997729b1b179fddd35f5a3", + "sha256:7b852817800eb02e109ae4a9cef2beda8dd50d98b76b6cfb7b5c0099d27b52d4", + "sha256:8cde829f14bd38f6da7b2954be0f2837043e8b8d7a9110ec5e318ae6bf706610", + "sha256:a2e3a39f43f0ce95204beb8fe0831199542ccab1e0c6e486a0b4947256215632", + "sha256:a86c962e211f37edd61d6e11bb4df7eddc4a519a38a856e20a6498c319efa6b0", + "sha256:a8705c5073fe3fcc297fb8e0b31aa794e05af6a329e81b7ca4ffecab7f2b95ef", + "sha256:b6aaeadf1e4866ca0fdf7bb4eed25e521ae21a7947c59f78154b24fc7abbe1dd", + "sha256:be62aeff8f2f054eff7725f502f6228298891fd648dc2630e03e44bf63e8cee0", + "sha256:c2edbb783c841e36ca0fa159f0ae97a88ce8137fb3a6cd82eae77349ba4b607b", + "sha256:cbe326f6d364375a8e5a8ccb7e9cd73f4b2f6dc3b2ed205633a0db8243e2a96a", + "sha256:d34fbb98ad0d6b563b95de852a284074514331e6b9da0a9fc894fb1cdae7a79e", + "sha256:d97a86937cf9970453c3b62abb55a6475f173347b4cde7f8dcdb48c8e1b9952d", + "sha256:dd53d7c4a69e766e4900f29db5872f5824a06827d594427cf1a4aa542818b796", + "sha256:df1889701e2dfd8ba4dc9b1a010f0a60950077fb5242bb92c8b5c7f1a6f2668a", + "sha256:fa1fe75b4a9e18b66ae7f0b122543c42debcf800aaafa0212aaff3ad273c2596" ], - "version": "==1.18.4" + "markers": "python_version >= '3.6'", + "version": "==1.19.0" }, "oauth2": { "hashes": [ @@ -457,58 +470,51 @@ }, "opencv-python": { "hashes": [ - "sha256:0f2e739c582e8c5e432130648bc6d66a56bc65f4cd9ff0bc7033033d2130c7a3", - "sha256:0f3d159ad6cb9cbd188c726f87485f0799a067a0a15f34c25d7b5c8db3cb2e50", - "sha256:167a6aff9bd124a3a67e0ec25d0da5ecdc8d96a56405e3e5e7d586c4105eb1bb", - "sha256:1b90d50bc7a31e9573a8da1b80fcd1e4d9c86c0e5f76387858e1b87eb8b0332b", - "sha256:2baf1213ae2fd678991f905d7b2b94eddfdfb5f75757db0f0b31eebd48ca200d", - "sha256:312dda54c7e809c20d7409418060ae0e9cdbe82975e7ced429eb3c234ffc0d4a", - "sha256:32384e675f7cefe707cac40a95eeb142d6869065e39c5500374116297cd8ca6d", - "sha256:5c50634dd8f2f866fd99fd939292ce10e52bef82804ebc4e7f915221c3b7e951", - "sha256:6841bb9cc24751dbdf94e7eefc4e6d70ec297952501954471299fd12ab67391c", - "sha256:68c1c846dd267cd7e293d3fc0bb238db0a744aa1f2e721e327598f00cb982098", - "sha256:703910aaa1dcd25a412f78a190fb7a352d9a64ee7d9a35566d786f3cc66ebf20", - "sha256:8002959146ed21959e3118c60c8e94ceac02eea15b691da6c62cff4787c63f7f", - "sha256:889eef049d38488b5b4646c48a831feed37c0fd44f3d83c05cff80f4baded145", - "sha256:8c76983c9ec3e4cf3a4c1d172ec4285332d9fb1c7194d724aff0c518437471ee", - "sha256:9cd9bd72f4a9743ef6f11f0f96784bd215a542e996db1717d4c2d3d03eb81a1b", - "sha256:a1a5517301dc8d56243a14253d231ec755b94486b4fff2ae68269bc941bb1f2e", - "sha256:a2b08aec2eacae868723136383d9eb84a33062a7a7ec5ec3bd2c423bd1355946", - "sha256:a8529a79233f3581a66984acd16bce52ab0163f6f77568dd69e9ee4956d2e1db", - "sha256:afbc81a3870739610a9f9a1197374d6a45892cf1933c90fc5617d39790991ed3", - "sha256:baeb5dd8b21c718580687f5b4efd03f8139b1c56239cdf6b9805c6946e80f268", - "sha256:db1d49b753e6e6c76585f21d09c7e9812176732baa9bddb64bc2fc6cd24d4179", - "sha256:e242ed419aeb2488e0f9ee6410a34917f0f8d62b3ae96aa3170d83bae75004e2", - "sha256:e36a8857be2c849e54009f1bee25e8c34fbc683fcd38c6c700af4cba5f8d57c2", - "sha256:e699232fd033ef0053efec2cba0a7505514f374ba7b18c732a77cb5304311ef9", - "sha256:eae3da9231d87980f8082d181c276a04f7a6fdac130cebd467390b96dd05f944", - "sha256:ee6814c94dbf1cae569302afef9dd29efafc52373e8770ded0db549a3b6e0c00", - "sha256:f01a87a015227d8af407161eb48222fc3c8b01661cdc841e2b86eee4f1a7a417" + "sha256:068928b9907b3d3acd53b129062557d6b0b8b324bfade77f028dbe4dfe482bf2", + "sha256:0e7c91718351449877c2d4141abd64eee1f9c8701bcfaf4e8627bd023e303368", + "sha256:1ab92d807427641ec45d28d5907426aa06b4ffd19c5b794729c74d91cd95090e", + "sha256:31d634dea1b47c231b88d384f90605c598214d0c596443c9bb808e11761829f5", + "sha256:5fdfc0bed37315f27d30ae5ae9bad47ec0a0a28c323739d39c8177b7e0929238", + "sha256:6fa8fac14dd5af4819d475f74af12d65fbbfa391d3110c3a972934a5e6507c24", + "sha256:78cc89ebc808886eb190626ee71ab65e47f374121975f86e4d5f7c0e3ce6bed9", + "sha256:7c7ba11720d01cb572b4b6945d115cb103462c0a28996b44d4e540d06e6a90fd", + "sha256:a37ee82f1b8ed4b4645619c504311e71ce845b78f40055e78d71add5fab7da82", + "sha256:aa3ca1f54054e1c6439fdf1edafa2a2b940a9eaac04a7b422a1cba9b2d7b9690", + "sha256:b9de3dd956574662712da8e285f0f54327959a4e95b96a2847d3c3f5ee7b96e2", + "sha256:c0087b428cef9a32d977390656d91b02245e0e91f909870492df7e39202645dd", + "sha256:d87e506ab205799727f0efa34b3888949bf029a3ada5eb000ff632606370ca6e", + "sha256:d8a55585631f9c9eca4b1a996e9732ae023169cf2f46f69e4518d67d96198226", + "sha256:dcb8da8c5ebaa6360c8555547a4c7beb6cd983dd95ba895bb78b86cc8cf3de2b", + "sha256:e2206bb8c17c0f212f1f356d82d72dd090ff4651994034416da9bf0c29732825", + "sha256:e3c57d6579e5bf85f564d6d48d8ee89868b92879a9232b9975d072c346625e92", + "sha256:ef89cbf332b9a735d8a82e9ff79cc743eeeb775ad1cd7100bc2aa2429b496f07", + "sha256:f45c1c3cdda1857bedd4dfe0bbd49c9419af0cc57f33490341edeae97d18f037", + "sha256:fb3c855347310788e4286b867997be354c55535597966ed5dac876d9166013a4" ], "index": "pypi", - "version": "==4.2.0.32" + "version": "==4.2.0.34" }, "pandas": { "hashes": [ - "sha256:07c1b58936b80eafdfe694ce964ac21567b80a48d972879a359b3ebb2ea76835", - "sha256:0ebe327fb088df4d06145227a4aa0998e4f80a9e6aed4b61c1f303bdfdf7c722", - "sha256:11c7cb654cd3a0e9c54d81761b5920cdc86b373510d829461d8f2ed6d5905266", - "sha256:12f492dd840e9db1688126216706aa2d1fcd3f4df68a195f9479272d50054645", - "sha256:167a1315367cea6ec6a5e11e791d9604f8e03f95b57ad227409de35cf850c9c5", - "sha256:1a7c56f1df8d5ad8571fa251b864231f26b47b59cbe41aa5c0983d17dbb7a8e4", - "sha256:1fa4bae1a6784aa550a1c9e168422798104a85bf9c77a1063ea77ee6f8452e3a", - "sha256:32f42e322fb903d0e189a4c10b75ba70d90958cc4f66a1781ed027f1a1d14586", - "sha256:387dc7b3c0424327fe3218f81e05fc27832772a5dffbed385013161be58df90b", - "sha256:6597df07ea361231e60c00692d8a8099b519ed741c04e65821e632bc9ccb924c", - "sha256:743bba36e99d4440403beb45a6f4f3a667c090c00394c176092b0b910666189b", - "sha256:858a0d890d957ae62338624e4aeaf1de436dba2c2c0772570a686eaca8b4fc85", - "sha256:863c3e4b7ae550749a0bb77fa22e601a36df9d2905afef34a6965bed092ba9e5", - "sha256:a210c91a02ec5ff05617a298ad6f137b9f6f5771bf31f2d6b6367d7f71486639", - "sha256:ca84a44cf727f211752e91eab2d1c6c1ab0f0540d5636a8382a3af428542826e", - "sha256:d234bcf669e8b4d6cbcd99e3ce7a8918414520aeb113e2a81aeb02d0a533d7f7" + "sha256:02f1e8f71cd994ed7fcb9a35b6ddddeb4314822a0e09a9c5b2d278f8cb5d4096", + "sha256:13f75fb18486759da3ff40f5345d9dd20e7d78f2a39c5884d013456cec9876f0", + "sha256:35b670b0abcfed7cad76f2834041dcf7ae47fd9b22b63622d67cdc933d79f453", + "sha256:4c73f373b0800eb3062ffd13d4a7a2a6d522792fa6eb204d67a4fad0a40f03dc", + "sha256:5759edf0b686b6f25a5d4a447ea588983a33afc8a0081a0954184a4a87fd0dd7", + "sha256:5a7cf6044467c1356b2b49ef69e50bf4d231e773c3ca0558807cdba56b76820b", + "sha256:69c5d920a0b2a9838e677f78f4dde506b95ea8e4d30da25859db6469ded84fa8", + "sha256:8778a5cc5a8437a561e3276b85367412e10ae9fff07db1eed986e427d9a674f8", + "sha256:9871ef5ee17f388f1cb35f76dc6106d40cb8165c562d573470672f4cdefa59ef", + "sha256:9c31d52f1a7dd2bb4681d9f62646c7aa554f19e8e9addc17e8b1b20011d7522d", + "sha256:ab8173a8efe5418bbe50e43f321994ac6673afc5c7c4839014cf6401bbdd0705", + "sha256:ae961f1f0e270f1e4e2273f6a539b2ea33248e0e3a11ffb479d757918a5e03a9", + "sha256:b3c4f93fcb6e97d993bf87cdd917883b7dab7d20c627699f360a8fb49e9e0b91", + "sha256:c9410ce8a3dee77653bc0684cfa1535a7f9c291663bd7ad79e39f5ab58f67ab3", + "sha256:f69e0f7b7c09f1f612b1f8f59e2df72faa8a6b41c5a436dde5b615aaf948f107", + "sha256:faa42a78d1350b02a7d2f0dbe3c80791cf785663d6997891549d0f86dc49125e" ], "index": "pypi", - "version": "==1.0.3" + "version": "==1.0.5" }, "pandas-ods-reader": { "hashes": [ @@ -536,38 +542,42 @@ }, "pillow": { "hashes": [ - "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be", - "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946", - "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837", - "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f", - "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00", - "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d", - "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533", - "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a", - "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358", - "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda", - "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435", - "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2", - "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313", - "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff", - "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317", - "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2", - "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614", - "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0", - "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386", - "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9", - "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636", - "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865" + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" ], "index": "pypi", - "version": "==7.0.0" + "version": "==7.2.0" }, "progressbar2": { "hashes": [ - "sha256:57594cc7ff7ff93138d6c09f650f9d31290b5d3bd1cf12339ced96a50c148749", - "sha256:ecf687696dd449067f69ef6730c4d4a0189db1f8d1aad9e376358354631d5b2c" + "sha256:13f228cf357f94cdef933c91c1e771e52e1b1931dbae48267be8fcdc2ae2ce36", + "sha256:27abf038efe5b1b5dd91ecc5f704bc88683c1e2a0b2c0fee04de80a648634a0c" ], - "version": "==3.51.3" + "version": "==3.51.4" }, "psutil": { "hashes": [ @@ -583,6 +593,7 @@ "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5", "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==5.7.0" }, "pybgpranking": { @@ -596,77 +607,80 @@ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.20" }, "pycryptodome": { "hashes": [ - "sha256:07024fc364869eae8d6ac0d316e089956e6aeffe42dbdcf44fe1320d96becf7f", - "sha256:09b6d6bcc01a4eb1a2b4deeff5aa602a108ec5aed8ac75ae554f97d1d7f0a5ad", - "sha256:0e10f352ccbbcb5bb2dc4ecaf106564e65702a717d72ab260f9ac4c19753cfc2", - "sha256:1f4752186298caf2e9ff5354f2e694d607ca7342aa313a62005235d46e28cf04", - "sha256:2fbc472e0b567318fe2052281d5a8c0ae70099b446679815f655e9fbc18c3a65", - "sha256:3ec3dc2f80f71fd0c955ce48b81bfaf8914c6f63a41a738f28885a1c4892968a", - "sha256:426c188c83c10df71f053e04b4003b1437bae5cb37606440e498b00f160d71d0", - "sha256:626c0a1d4d83ec6303f970a17158114f75c3ba1736f7f2983f7b40a265861bd8", - "sha256:767ad0fb5d23efc36a4d5c2fc608ac603f3de028909bcf59abc943e0d0bc5a36", - "sha256:7ac729d9091ed5478af2b4a4f44f5335a98febbc008af619e4569a59fe503e40", - "sha256:83295a3fb5cf50c48631eb5b440cb5e9832d8c14d81d1d45f4497b67a9987de8", - "sha256:8be56bde3312e022d9d1d6afa124556460ad5c844c2fc63642f6af723c098d35", - "sha256:8f06556a8f7ea7b1e42eff39726bb0dca1c251205debae64e6eebea3cd7b438a", - "sha256:9230fcb5d948c3fb40049bace4d33c5d254f8232c2c0bba05d2570aea3ba4520", - "sha256:9378c309aec1f8cd8bad361ed0816a440151b97a2a3f6ffdaba1d1a1fb76873a", - "sha256:9977086e0f93adb326379897437373871b80501e1d176fec63c7f46fb300c862", - "sha256:9a94fca11fdc161460bd8659c15b6adef45c1b20da86402256eaf3addfaab324", - "sha256:9c739b7795ccf2ef1fdad8d44e539a39ad300ee6786e804ea7f0c6a786eb5343", - "sha256:b1e332587b3b195542e77681389c296e1837ca01240399d88803a075447d3557", - "sha256:c109a26a21f21f695d369ff9b87f5d43e0d6c768d8384e10bc74142bed2e092e", - "sha256:c818dc1f3eace93ee50c2b6b5c2becf7c418fa5dd1ba6fc0ef7db279ea21d5e4", - "sha256:cff31f5a8977534f255f729d5d2467526f2b10563a30bbdade92223e0bf264bd", - "sha256:d4f94368ce2d65873a87ad867eb3bf63f4ba81eb97a9ee66d38c2b71ce5a7439", - "sha256:d61b012baa8c2b659e9890011358455c0019a4108536b811602d2f638c40802a", - "sha256:d6e1bc5c94873bec742afe2dfadce0d20445b18e75c47afc0c115b19e5dd38dd", - "sha256:ea83bcd9d6c03248ebd46e71ac313858e0afd5aa2fa81478c0e653242f3eb476", - "sha256:ed5761b37615a1f222c5345bbf45272ae2cf8c7dff88a4f53a1e9f977cbb6d95", - "sha256:f011cd0062e54658b7086a76f8cf0f4222812acc66e219e196ea2d0a8849d0ed", - "sha256:f1add21b6d179179b3c177c33d18a2186a09cc0d3af41ff5ed3f377360b869f2", - "sha256:f655addaaaa9974108d4808f4150652589cada96074c87115c52e575bfcd87d5" + "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", + "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", + "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", + "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", + "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", + "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", + "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", + "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", + "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", + "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", + "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", + "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", + "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", + "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", + "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", + "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", + "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", + "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", + "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", + "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", + "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", + "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", + "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", + "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", + "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", + "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", + "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", + "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", + "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", + "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94" ], - "version": "==3.9.7" + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.9.8" }, "pycryptodomex": { "hashes": [ - "sha256:1537d2d15b604b303aef56e7f440895a1c81adbee786b91f1f06eddc34da5314", - "sha256:1d20ab8369b7558168fc014a0745c678613f9f486dae468cca2d68145196b8a4", - "sha256:1ecc9db7409db67765eb008e558879d298406642d33ade43a6488224d23e8081", - "sha256:37033976f72af829fe15f7fe5fe1dbed308cc43a98d9dd9d2a0a76de8ca5ee78", - "sha256:3c3dd9d4c9c1e279d3945ae422895c901f98987333acc132dc094faf52afec35", - "sha256:3c9b3fba037ea52c626060c5a87ee6de7e86c99e8a7c6ee07302539985d2bd64", - "sha256:45ee555fc5e28c119a46d44ce373f5237e54a35c61b750fb3a94446b09855dbc", - "sha256:4c93038ac011b36512cb0bf2ee3e2aec774e8bc81021d015917c89fe02bb0ee5", - "sha256:50163324834edd0c9ce3e4512ded3e221c969086e10fdd5d3fdcaadac5e24a78", - "sha256:59b0ea9cda5490f924771456912a225d8d9e678891f9f986661af718534719b2", - "sha256:5cf306a17cccc327a33cdc3845629fa13f4573a4ec620ed607c79cf6785f2e27", - "sha256:5fff8da399af16a1855f58771223acbbdac720b9969cd03fc5013d2e9a7bd9a4", - "sha256:68650ce5b9f7152b8283302a4617269f821695a612692640dd247bd12ab21c0b", - "sha256:6b3a9a562688996f760b5077714c3ab8b62ca56061b6e9ab7906841e43e19f91", - "sha256:7e938ed51a59e29431ea86fab60423ada2757728db0f78952329fa02a789bd31", - "sha256:87aa70daad6f039e814790a06422a3189311198b674b62f13933a2bdcb6b1bcc", - "sha256:99be3a1df2b2b9f731ebe1c264a2c07c465e71cee68e35e1640b645b5213a755", - "sha256:a3f2908666e6f74b8c4893f86dd02e16170f50e4a78ae7f3468b6208d54bc205", - "sha256:ae3d44a639fd11dbdeca47e35e94febb1ee8bc15daf26673331add37146e0b85", - "sha256:afb4c2fa3c6f492fd9a8b38d76e13f32d429b8e5e1e00238309391b5591cde0d", - "sha256:b1515ce3a8a2c3fa537d137c5ca5f8b7a902044d04e07d7c3aa26c3e026120fb", - "sha256:bf391b377413a197000b43ef2b74359974d8927d329a897c9f5ba7b63dca7b9c", - "sha256:c436919117c23355740c669f89720673578b9aa4569bbfe105f6c10101fc1966", - "sha256:d2c3c280975638e2a2c2fd9cb36ab111980219757fa163a2755594b9448e4138", - "sha256:e585d530764c459cbd5d460aed0288807bb881f376ca9a20e653645217895961", - "sha256:e76e6638ead4a7d93262a24218f0ff3ff74de6b6c823b7e19dccb31b6a481978", - "sha256:ebfc2f885cafda076c31ae30fa0dd81e7e919ec34059a88d3018ed66e83fcce3", - "sha256:f5797a39933a3d41526da60856735e6684b2b71a8ca99d5f79555ca121be2f4b", - "sha256:f7e5fc5e124200b19a14be33fb0099e956e6ebb5e25d287b0829ef0a78ed76c7", - "sha256:fb350e31e55211fec8ddc89fc0256f3b9bc3b44b68a8bde1cf44b3b4e80c0e42" + "sha256:06f5a458624c9b0e04c0086c7f84bcc578567dab0ddc816e0476b3057b18339f", + "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", + "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", + "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", + "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", + "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", + "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", + "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", + "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", + "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", + "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", + "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", + "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", + "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", + "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", + "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", + "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", + "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", + "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", + "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", + "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", + "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", + "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", + "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", + "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", + "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060", + "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", + "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", + "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb" ], - "version": "==3.9.7" + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.9.8" }, "pydeep": { "hashes": [ @@ -681,10 +695,11 @@ }, "pyeupi": { "hashes": [ - "sha256:35b0e6b430f23ecd303f7cc7a8fe5147cf2509a5b2254eaf9695392c0af02901" + "sha256:2309c61ac2ef0eafabd6e9f32a0078069ffbba0e113ebc6b51cffc1869094472", + "sha256:a0798a4a52601b0840339449a1bbf2aa2bc180d8f82a979022954e05fcb5bfba" ], "index": "pypi", - "version": "==1.0" + "version": "==1.1" }, "pygeoip": { "hashes": [ @@ -708,10 +723,12 @@ "pymisp": { "editable": true, "extras": [ - "fileobjects,openioc,virustotal,pdfexport" + "fileobjects", + "openioc", + "pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "b5b40ae2c5225a4b349c26294cfc012309a61352" + "ref": "ec28820cf491ca7d385477996afa0547eb6b6830" }, "pyonyphe": { "editable": true, @@ -730,6 +747,7 @@ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.4.7" }, "pypdns": { @@ -755,16 +773,17 @@ }, "pytesseract": { "hashes": [ - "sha256:1041f83ad3eed768df145d85275bb9a611861d31fcfe30aa4bfeb79d6529b452" + "sha256:afd8a5cdf8ab5d35690efbe71cbf5f89419f668ea8dde7649149815d5c5a899a" ], "index": "pypi", - "version": "==0.3.3" + "version": "==0.3.4" }, "python-dateutil": { "hashes": [ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.8.1" }, "python-docx": { @@ -829,11 +848,11 @@ }, "pyzipper": { "hashes": [ - "sha256:e77164f37acee2160569896347dfca71f0f9b352c351dfa3981e1595a9ba0902", - "sha256:fb42f41525979ef9ddf8c2b1fdd8cb2216057d8cede250f21d469f0b269479cf" + "sha256:49813f1d415bdd7c87064009b9270c6dd0a96da770cfe57df2c6d2d84a6c085a", + "sha256:bfdc65f616278b38ef03c6ea5a1aca7499caf98cbfcd47fc44f73e68f4307145" ], "markers": "python_version >= '3.5'", - "version": "==0.3.1" + "version": "==0.3.3" }, "rdflib": { "hashes": [ @@ -844,67 +863,68 @@ }, "redis": { "hashes": [ - "sha256:2ef11f489003f151777c064c5dbc6653dfb9f3eade159bcadc524619fddc2242", - "sha256:6d65e84bc58091140081ee9d9c187aab0480097750fac44239307a3bdf0b1251" + "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", + "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" ], - "version": "==3.5.2" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==3.5.3" }, "reportlab": { "hashes": [ - "sha256:072da175f9586fd0457242d7eb4ccf8284b65f8c4ec33ec4fa39c511ca2c6e10", - "sha256:12b1deee658b6a9766e7aca061dfa52c396e984fb328178480ae11ff7717cda4", - "sha256:28c56f85900bc9632ac6c44f71629a34da3a7da0904a19ecbf69ea7aec976bf3", - "sha256:2ac6bf19ecc60149895273932910b7cde61bcfc6701326094078eee489265de5", - "sha256:31feebbfd476201e82aecf750201acb1ea7d3b29217d2e0ca0a297d1189a78af", - "sha256:330aa2b493c9a42b28c65b5b4c7de4c4f372b1292f082b1a097d56b12e2ba097", - "sha256:39ae8212a07a18f0e3ee0a3bca6e5a37abac470f934e5a1a117209f989618373", - "sha256:3af29daf6681fb1c6abbe8a948c6cdf241c7d9bcdce4b881076323e70b44865c", - "sha256:3d33f934e13263fac098672840f8e0959643b747a516a50792868c3ae7251c37", - "sha256:3ea95bcfcba08eb4030e3b62efc01ff9e547eea7887311f00685c729cabce038", - "sha256:45f4aab315f301b4c184f1ee5fb4234fd1388335b191cf827ea977a98b0158dc", - "sha256:497c8d56d2f98561b78d9e21d9a2a39ab9e2dd81db699f1cddcba744ba455330", - "sha256:4f4463f1591cf66996a292835f04a521470cf9a479724017a9227125f49f7492", - "sha256:553658b979b3e8dd662cd8c37d1955cc832b2c000f4cb6d076d8401d771dd85f", - "sha256:5a8430eed5fc7d15c868fdf5673c94440710e7d1a77ea5bbd4f634e3e6fb5f9c", - "sha256:5cc32b8ce94c9345fe59af2cbf47edb1c1615304b67f522957666485f87694f7", - "sha256:5d851a20981e6ea29b643e59807997ca96ceeded4bf431ba9618171d8e383091", - "sha256:64f7cfa75b9b9a1eebf2a3fe5667a01953e1cb8946b0d14f165b9381ec2fdbaf", - "sha256:650ec96cc3cb86ae27987db5d36abe530ef45ec67032c4633c776dd3ab016ca4", - "sha256:6771e0875203d130f1f9c9c04f26084178cb4720552580af8b393cf70c4943a5", - "sha256:67f5b94ba44a4e764974b0ee9d2f574c593c11ec1cb19aedd17a1bebc35a597e", - "sha256:6d6815a925c071a0b887c968d39527e9b3db962a151d2aabdd954beafd4431ad", - "sha256:6e6e3041b742a73c71c0dc49875524338998cbf6a498077e40d4589f8448f3ed", - "sha256:6fb58a2fdc725a601d225f377b3e1cc3837f8f560cc6c2ceeb8028010031fd65", - "sha256:7c36e52452147e64a48a05ac56340b45aa3f0c64f2b2e38145ea15190c369621", - "sha256:8194698254932234a1164694a5b8c84d8010db6ff71a8985c6133d21ed9767ea", - "sha256:9c21f202697a6cea57b9d716288fc919d99cbabeb30222eebfc7ff77eac32744", - "sha256:9ffbdbac35c084c2026c4d978498017b5433a61adfe6c1e500c506d38707b39c", - "sha256:ab6acd99073081d708339e26475e93fe48139233a2ab7f43fc54560e1e00155a", - "sha256:bd1c855249f5508a50e3ddc7b4e957e4a537597bd41e66e71bdc027bbcfa7534", - "sha256:c14de6b939ad2ea63e4149e3e4eae1089e20afae1ef805345f73193f25ac9e5f", - "sha256:cb24edd3e659c783abee1162559cc2a94537974fc73d73da7e3a7021b1ab9803", - "sha256:d144680292a868cbfe02db25eecbf53623af02e42ff05822439f1434156e7863", - "sha256:db5c44a77f10357f5c2c25545b7fbc009616274f9ac1876b00398693d0fc4324", - "sha256:e326b2d48ccaf17322f86c23cd78900e50facf27b93ce50e4a2902a5f31ac343", - "sha256:e6c3fc2866b853b6b9d4b5d79cfff89c5687fc70a155a05dcfdd278747d441db", - "sha256:ef817701f45bb6974cfc0a488fd9a76c4190948c456234490174d1f2112b0a2c", - "sha256:eff08b53ab4fa2adf4b763e56dd1369d6c1cb2a18d3daee7a5f53b25198c0a36", - "sha256:f18ad0212b7204f5fae37682ec4760a11e1130c294294cfcd900d202d90ed9d9", - "sha256:f7e4e8adc959dd65e127ae0865fb278d40b34ee2ae8e41e2c5fa8dc83cea273b" + "sha256:0f0c2d98e213d51ae527c0301364d3376cb05f6c47251368a9abd4c3197fcefa", + "sha256:1425c7ea60b8691a881ae21ea0f6907a1dc480d84204ccbfea6da41fbee8f594", + "sha256:204f1d245875ab3d076b37c1a18ac8d2e3222842e13cfa282bcd95282be239e5", + "sha256:21627b57249303bf9b5a633099d058ae9f8625fd6f90cfe79348c48fd5a242cd", + "sha256:2e8e3242f80b79f2470f1b5979abbdb41f31b1333543b830749100342f837d40", + "sha256:2eced06dec3f36135c626b9823649ef9cac95c5634d1bc743a15ee470027483b", + "sha256:3472aa0b74a3b2f252dce823f3c3ba6af8a24de0c1729441deaaf50bed6de9f9", + "sha256:3f0353ffefd3afc0061f4794ef608d6c6f32e69816885f4d45c625c20d8eaf5b", + "sha256:4a9f4540a8eddf56d900ceeb8136bd0ca866c208ba3dcbcde73f07405dbadfba", + "sha256:4eea1afb4aa89780734f44175508edff82928fdf460c9bd60bc719dd99041dc3", + "sha256:5803ffebd36de1ada417f50ce65d379ea5a0bf1a2e8f5d5710a031b3b349b726", + "sha256:58f5f72fc8e5932dedcf24789908a81c6b1e13ea4d63bd9a9a39dc698d8c3321", + "sha256:5b588e5f251c76a8d3589023d1c369c7968e0efe2b38ad5948f665edbf6f9e8b", + "sha256:5d922768fe11a58d80694852aba7389d613c15eb1871c5581a2f075996873d57", + "sha256:5d98f297c5cdd5bc0ccf5697c20b03602ee3378c97938d20312662b27cd9a1d6", + "sha256:66d1d96e97a562614943ecb9daf438e392b3d0b033bd5f4a8098ab616dd877da", + "sha256:670650970c7ba7164cf6340bcd182e7e933eff5d65183af98ee77b40cc25a438", + "sha256:67bb95af7bc8ad7925d299f310d15d556d3e7026fe1b60d8e290454604ae0a85", + "sha256:9c999f5d1a600c4970ba293789b6da14e02e3763a8d3d9abe42dcafa8a5318e9", + "sha256:9d62bef5347063a984e63410fa5a69f1d2cc2fdf8d6ed3d0b9d4ea2ccb4b4154", + "sha256:a14a0d603727b6be2e549c52dd42678ab2d06d2721d4580199e3161843e59298", + "sha256:a3a17b46ff1a15eb29370e11796d8914ef4ea67471bdbc4aa9a9eb9284f4e44c", + "sha256:a6d3e20beeba3fd68cec73b8c0785bfa648c06ac76d1f142c60ccb1a8d2506b6", + "sha256:ad7d7003c732f2be42580e3906e92bd9d2aca5e098898c597554be9ca627fad5", + "sha256:af0ee7b50b85543b68b043e61271963ff5671e564e1d620a404c24a24d4f537c", + "sha256:b3eec55274f5ead7e3af2bf0c01b481ffe1b4c6a7dae42b63d85543e9f2f9a0f", + "sha256:b48c21d43a7ab956954591ce3f71db92ce542bb7428db09734425e2b77ac3142", + "sha256:b761905ab85beb79cf7929c9a019f30ad65664e5733d57a30a995e7b9bef06d1", + "sha256:bbae2f054d0f234c3382076efa337802997aca0f3f664e314f65eefb9d694fa9", + "sha256:bd4157d0bc40fb72bb676fc745fdd648022cccaf4ccfbb291af7f48831d0d5d9", + "sha256:bf74cfabf332034f42a54938eb335543cbf92790170300dbe236ba83b7601cd0", + "sha256:c253c8571db2df3886e390a2bfbe917222953054f4643437373b824f64b013cd", + "sha256:ce1277a6acbc62e9966f410f2596ac533ee0cd5df9b69d5fe4406338a169b7d8", + "sha256:ce8f56987e0e456063e311f066a81496b8b9626c846f2cb0ebb554d1a5f40839", + "sha256:d6264a0589ba8032d9c3bdca9a3e87a897ede09b7f6a8ad5e83b57573212e01e", + "sha256:e6fa0c97e3929d00db27e8cf3b2b5771e94f5f179086c4b0e3213dff53637372", + "sha256:f0930f2b6dddd477b3331ec670171a4662336aac1a778e1a30e980a5cbf40b17", + "sha256:f8cb2b4b925ca6b6e4fdefd288a707776ac686c45034f34d4c952f122d11c40b", + "sha256:f9b71539f518323d95850405c49c01fc3d2f0f0b9f3e157de6d2786804fb28a4", + "sha256:fc488e661f99c915362e0373218f8727cecf888eb1b0eb3a8fe1af624a1b9776" ], "index": "pypi", - "version": "==3.5.42" + "version": "==3.5.44" }, "requests": { "extras": [ "security" ], "hashes": [ - "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", - "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" ], "index": "pypi", - "version": "==2.23.0" + "version": "==2.24.0" }, "requests-cache": { "hashes": [ @@ -915,25 +935,26 @@ }, "shodan": { "hashes": [ - "sha256:a9f098c2d24cf685b6d4a4bd46c7f56653c84f777f20d1a853cfd7672f68f35d" + "sha256:31b0740ffaf7c5196a26a0b1edf7d271dffe54ea52bb1b34ba87aa231b5c339b" ], "index": "pypi", - "version": "==1.22.0" + "version": "==1.23.0" }, "sigmatools": { "hashes": [ - "sha256:6b28b30efbaa5cbb967927ea4e31c617cc91a210aad6e0a00cbe11d4ea48c3cd", - "sha256:85dfae6479d245e7e7936f02d754954ea16e2c2f757035d0b329571fa048febc" + "sha256:5453717e452aa7860c5e6ac80bcee4f398d70956fc2ee9859bc7255067da8736", + "sha256:cdfeb8200c09c0a40ea1a015e57f3b8e2ba62a28352ca05fa015674f640871e3" ], "index": "pypi", - "version": "==0.16.0" + "version": "==0.17.0" }, "six": { "hashes": [ - "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", - "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "version": "==1.14.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" }, "socketio-client": { "hashes": [ @@ -946,11 +967,14 @@ "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55", "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232" ], + "markers": "python_version >= '3.5'", "version": "==2.0.1" }, "sparqlwrapper": { "hashes": [ + "sha256:17ec44b08b8ae2888c801066249f74fe328eec25d90203ce7eadaf82e64484c7", "sha256:357ee8a27bc910ea13d77836dbddd0b914991495b8cc1bf70676578155e962a8", + "sha256:8cf6c21126ed76edc85c5c232fd6f77b9f61f8ad1db90a7147cdde2104aff145", "sha256:c7f9c9d8ebb13428771bc3b6dee54197422507dcc3dea34e30d5dcfc53478dec", "sha256:d6a66b5b8cda141660e07aeb00472db077a98d22cb588c973209c7336850fb3c" ], @@ -984,13 +1008,35 @@ "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a", "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740" ], + "markers": "python_version >= '3.5'", "version": "==6.0.4" }, + "trustar": { + "hashes": [ + "sha256:73336b94012427b66ee61db65fc3c2cea2ed743beaa56cdd5a4c1674ef1a7660" + ], + "index": "pypi", + "version": "==0.3.29" + }, + "tzlocal": { + "hashes": [ + "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44", + "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4" + ], + "version": "==2.1" + }, + "unicodecsv": { + "hashes": [ + "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc" + ], + "version": "==0.14.1" + }, "url-normalize": { "hashes": [ "sha256:1709cb4739e496f9f807a894e361915792f273538e250b1ab7da790544a665c3", "sha256:1bd7085349dcdf06e52194d0f75ff99fff2eeed0da85a50e4cc2346452c1b8bc" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==1.4.2" }, "urlarchiver": { @@ -1005,12 +1051,13 @@ "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.25.9" }, "uwhois": { "editable": true, "git": "https://github.com/Rafiot/uwhoisd.git", - "ref": "411572840eba4c72dc321c549b36a54ed5cea9de", + "ref": "783bba09b5a6964f25566089826a1be4b13f2a22", "subdirectory": "client" }, "validators": { @@ -1038,11 +1085,11 @@ }, "wand": { "hashes": [ - "sha256:598e13e46779e48fcecba7b37fd9d61fcdd1e70007ccba5d5b2e731186a2ec2e", - "sha256:6eaca78e53fbe329b163f0f0b28f104de98edbd69a847268cc5d6a6e392b9b28" + "sha256:d5b75ac13d7485032970926415648586eafeeb1eb62ed6ebd0778358cf9d70e0", + "sha256:df0780b1b54938a43d29279a6588fde11e349550c8958a673d57c26a3e6de7f1" ], "index": "pypi", - "version": "==0.5.9" + "version": "==0.6.1" }, "websocket-client": { "hashes": [ @@ -1067,10 +1114,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:488e1988ab16ff3a9cd58c7656d0a58f8abe46ee58b98eecea78c022db28656b", - "sha256:97ab487b81534415c5313154203f3e8a637d792b1e6a8201e8f7f71da0203c2a" + "sha256:828b3285fc95105f5b1946a6a015b31cf388bd5378fdc6604e4d1b7839df2e77", + "sha256:82a3b0e73e3913483da23791d1a25e4d2dbb3837d1be4129473526b9a270a5cc" ], - "version": "==1.2.8" + "version": "==1.2.9" }, "yara-python": { "hashes": [ @@ -1109,6 +1156,7 @@ "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080", "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" ], + "markers": "python_version >= '3.5'", "version": "==1.4.2" } }, @@ -1118,14 +1166,15 @@ "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==19.3.0" }, "certifi": { "hashes": [ - "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", - "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" ], - "version": "==2020.4.5.1" + "version": "==2020.6.20" }, "chardet": { "hashes": [ @@ -1136,11 +1185,12 @@ }, "codecov": { "hashes": [ - "sha256:09fb045eb044a619cd2b9dacd7789ae8e322cb7f18196378579fd8d883e6b665", - "sha256:aeeefa3a03cac8a78e4f988e935b51a4689bb1f17f20d4e827807ee11135f845" + "sha256:491938ad774ea94a963d5d16354c7299e90422a33a353ba0d38d0943ed1d5091", + "sha256:b67bb8029e8340a7bf22c71cbece5bd18c96261fdebc2f105ee4d5a005bc8728", + "sha256:d8b8109f44edad03b24f5f189dac8de9b1e3dc3c791fa37eeaf8c7381503ec34" ], "index": "pypi", - "version": "==2.0.22" + "version": "==2.1.7" }, "coverage": { "hashes": [ @@ -1176,29 +1226,24 @@ "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==5.1" }, - "entrypoints": { - "hashes": [ - "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", - "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451" - ], - "version": "==0.3" - }, "flake8": { "hashes": [ - "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb", - "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca" + "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c", + "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208" ], "index": "pypi", - "version": "==3.7.9" + "version": "==3.8.3" }, "idna": { "hashes": [ - "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", - "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], - "version": "==2.9" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" }, "mccabe": { "hashes": [ @@ -1209,10 +1254,11 @@ }, "more-itertools": { "hashes": [ - "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be", - "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982" + "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5", + "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2" ], - "version": "==8.3.0" + "markers": "python_version >= '3.5'", + "version": "==8.4.0" }, "nose": { "hashes": [ @@ -1228,6 +1274,7 @@ "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.4" }, "pluggy": { @@ -1235,75 +1282,82 @@ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.13.1" }, "py": { "hashes": [ - "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa", - "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0" + "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", + "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" ], - "version": "==1.8.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.9.0" }, "pycodestyle": { "hashes": [ - "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", - "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" + "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", + "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" ], - "version": "==2.5.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.6.0" }, "pyflakes": { "hashes": [ - "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", - "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" ], - "version": "==2.1.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.2.0" }, "pyparsing": { "hashes": [ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.4.7" }, "pytest": { "hashes": [ - "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172", - "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970" + "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", + "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8" ], "index": "pypi", - "version": "==5.4.1" + "version": "==5.4.3" }, "requests": { "extras": [ "security" ], "hashes": [ - "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", - "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" ], "index": "pypi", - "version": "==2.23.0" + "version": "==2.24.0" }, "six": { "hashes": [ - "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", - "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "version": "==1.14.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" }, "urllib3": { "hashes": [ "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.25.9" }, "wcwidth": { "hashes": [ - "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", - "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" ], - "version": "==0.1.9" + "version": "==0.2.5" } } } From 8e4c688dcef5e1ee9808f29fa77b32ec3f45ef51 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 3 Jul 2020 10:10:24 +0200 Subject: [PATCH 159/220] fix: Fixed list of sigma backends --- misp_modules/modules/expansion/sigma_queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/sigma_queries.py b/misp_modules/modules/expansion/sigma_queries.py index b7c871d..d17a100 100644 --- a/misp_modules/modules/expansion/sigma_queries.py +++ b/misp_modules/modules/expansion/sigma_queries.py @@ -12,7 +12,7 @@ mispattributes = {'input': ['sigma'], 'output': ['text']} moduleinfo = {'version': '0.1', 'author': 'Christian Studer', 'module-type': ['expansion', 'hover'], 'description': 'An expansion hover module to display the result of sigma queries.'} moduleconfig = [] -sigma_targets = ('es-dsl', 'es-qs', 'graylog', 'kibana', 'xpack-watcher', 'logpoint', 'splunk', 'grep', 'wdatp', 'splunkxml', 'arcsight', 'qualys') +sigma_targets = ('es-dsl', 'es-qs', 'graylog', 'kibana', 'xpack-watcher', 'logpoint', 'splunk', 'grep', 'mdatp', 'splunkxml', 'arcsight', 'qualys') def handler(q=False): From c91a61110a125bf7fd9369221339694b11011847 Mon Sep 17 00:00:00 2001 From: johannesh Date: Thu, 23 Jul 2020 12:28:56 +0200 Subject: [PATCH 160/220] Add Recorded Future expansion module --- README.md | 1 + misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/recordedfuture.py | 280 ++++++++++++++++++ 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/recordedfuture.py diff --git a/README.md b/README.md index 67573da..77ece38 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [pptx-enrich](misp_modules/modules/expansion/pptx_enrich.py) - an enrichment module to get text out of PowerPoint document into MISP (using free-text parser). * [qrcode](misp_modules/modules/expansion/qrcode.py) - a module decode QR code, barcode and similar codes from an image and enrich with the decoded values. * [rbl](misp_modules/modules/expansion/rbl.py) - a module to get RBL (Real-Time Blackhost List) values from an attribute. +* [recordedfuture](misp_modules/modules/expansion/recordedfuture.py) - a hover and expansion module for enriching MISP attributes with threat intelligence from Recorded Future. * [reversedns](misp_modules/modules/expansion/reversedns.py) - Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes. * [securitytrails](misp_modules/modules/expansion/securitytrails.py) - an expansion module for [securitytrails](https://securitytrails.com/). * [shodan](misp_modules/modules/expansion/shodan.py) - a minimal [shodan](https://www.shodan.io/) expansion module. diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index dbd3473..14d5499 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,4 +18,4 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', - 'trustar_enrich'] + 'trustar_enrich', 'recordedfuture'] diff --git a/misp_modules/modules/expansion/recordedfuture.py b/misp_modules/modules/expansion/recordedfuture.py new file mode 100644 index 0000000..c42a42b --- /dev/null +++ b/misp_modules/modules/expansion/recordedfuture.py @@ -0,0 +1,280 @@ +import json +import logging +import requests +from urllib.parse import quote +from pymisp import MISPAttribute, MISPEvent, MISPTag, MISPObject + +moduleinfo = {'version': '1.0', 'author': 'Recorded Future', + 'description': 'Module to retrieve data from Recorded Future', + 'module-type': ['expansion', 'hover']} + +moduleconfig = ['token'] + +misperrors = {'error': 'Error'} + +mispattributes = {'input': ['ip', 'ip-src', 'ip-dst', 'domain', 'hostname', 'md5', 'sha1', 'sha256', + 'uri', 'url', 'vulnerability', 'weakness'], + 'output': ['ip', 'ip-src', 'ip-dst', 'domain', 'hostname', 'md5', 'sha1', 'sha256', + 'uri', 'url', 'vulnerability', 'weakness', 'email-src', 'text'], + 'format': 'misp_standard'} + +LOGGER = logging.getLogger('recorded_future') +LOGGER.setLevel(logging.INFO) + + +def rf_lookup(api_token: str, category: str, ioc: str) -> requests.Response: + """Do a lookup call using Recorded Future's ConnectAPI.""" + auth_header = {"X-RFToken": api_token} + parsed_ioc = quote(ioc, safe='') + url = f'https://api.recordedfuture.com/v2/{category}/{parsed_ioc}?fields=risk%2CrelatedEntities' + response = requests.get(url, headers=auth_header) + response.raise_for_status() + return response + + +class GalaxyFinder: + """A class for finding MISP galaxy matches to Recorded Future data.""" + def __init__(self): + self.session = requests.Session() + self.sources = { + 'RelatedThreatActor': ['https://raw.githubusercontent.com/MISP/misp-galaxy/' + 'main/clusters/threat-actor.json'], + 'RelatedMalware': ['https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/banker.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/botnet.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/exploit-kit.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/rat.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/ransomware.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/malpedia.json'] + } + self.galaxy_clusters = {} + + def pull_galaxy_cluster(self, related_type: str): + """Fetches galaxy clusters for the related_type from the remote json files specified as self.sources.""" + # Only fetch clusters if not fetched previously + if not self.galaxy_clusters.get(related_type): + for source in self.sources.get(related_type): + response = self.session.get(source) + if response.ok: + name = source.split('/')[-1].split('.')[0] + self.galaxy_clusters[related_type] = {name: response.json()} + else: + LOGGER.info(f'pull_galaxy_cluster failed for source: {source},' + f' got response: {response}, {response.reason}.') + + def find_galaxy_match(self, indicator: str, related_type: str) -> str: + """Searches the clusters of the related_type for a match with the indicator. + :returns the first matching galaxy string or an empty string if no galaxy match is found. + """ + self.pull_galaxy_cluster(related_type) + try: + for cluster_name, cluster in self.galaxy_clusters[related_type].items(): + for value in cluster['values']: + try: + if indicator in value['meta']['synonyms'] or indicator in value['value']: + value = value['value'] + return f'misp-galaxy:{cluster_name}="{value}"' + except KeyError: + pass + except KeyError: + pass + return '' + + +class RFColors: + """Class for setting signature RF-colors.""" + def __init__(self): + self.rf_white = '#CCCCCC' + self.rf_yellow = '#FFCE00' + self.rf_red = '#CF0A2C' + + def riskscore_color(self, risk_score: int) -> str: + """Returns appropriate hex-colors according to risk score.""" + risk_score = int(risk_score) + if risk_score < 25: + return self.rf_white + elif risk_score < 65: + return self.rf_yellow + else: + return self.rf_red + + def riskrule_color(self, risk_rule_criticality: int) -> str: + """Returns appropriate hex-colors according to risk rule criticality.""" + risk_rule_criticality = int(risk_rule_criticality) + if risk_rule_criticality == 1: + return self.rf_white + elif risk_rule_criticality == 2: + return self.rf_yellow + else: # risk_rule_criticality == 3 or 4 + return self.rf_red + + +class RFEnricher: + """Class for enriching an attribute with data from Recorded Future. + The enrichment data is returned as a custom MISP object. + """ + def __init__(self, api_token: str, attribute_props: dict): + self.api_token = api_token + self.event = MISPEvent() + self.enrichment_object = MISPObject('Recorded Future Enrichment') + self.enrichment_object.from_dict(**{'meta-category': 'misc', + 'description': 'An object containing the enriched attribute and related ' + 'entities from Recorded Future.', + 'distribution': 0}) + + # Create a copy of enriched attribute to add tags to + temp_attr = MISPAttribute() + temp_attr.from_dict(**attribute_props) + self.enriched_attribute = MISPAttribute() + self.enriched_attribute.from_dict(**{'value': temp_attr.value, 'type': temp_attr.type, 'distribution': 0}) + + self.related_attributes = [] + self.color_picker = RFColors() + self.galaxy_finder = GalaxyFinder() + + # Mapping from MISP-type to RF-type + self.type_to_rf_category = {'ip': 'ip', 'ip-src': 'ip', 'ip-dst': 'ip', + 'domain': 'domain', 'hostname': 'domain', + 'md5': 'hash', 'sha1': 'hash', 'sha256': 'hash', + 'uri': 'url', 'url': 'url', + 'vulnerability': 'vulnerability', 'weakness': 'vulnerability'} + + # Related entities from RF portrayed as related attributes in MISP + self.related_attribute_types = ['RelatedIpAddress', 'RelatedInternetDomainName', 'RelatedHash', + 'RelatedEmailAddress', 'RelatedCyberVulnerability'] + # Related entities from RF portrayed as tags in MISP + self.galaxy_tag_types = ['RelatedMalware', 'RelatedThreatActor'] + + def enrich(self): + """Run the enrichment.""" + category = self.type_to_rf_category.get(self.enriched_attribute.type) + + try: + response = rf_lookup(self.api_token, category, self.enriched_attribute.value) + json_response = json.loads(response.content) + except requests.HTTPError as error: + misperrors['error'] = f'Error when requesting data from Recorded Future. ' \ + f'{error.response} : {error.response.reason}' + raise error + + try: + # Add risk score and risk rules as tags to the enriched attribute + risk_score = json_response['data']['risk']['score'] + hex_color = self.color_picker.riskscore_color(risk_score) + tag_name = f'recorded-future:risk-score="{risk_score}"' + self.add_tag(tag_name, hex_color) + for evidence in json_response['data']['risk']['evidenceDetails']: + risk_rule = evidence['rule'] + criticality = evidence['criticality'] + hex_color = self.color_picker.riskrule_color(criticality) + tag_name = f'recorded-future:risk-rule="{risk_rule}"' + self.add_tag(tag_name, hex_color) + + # Retrieve related entities + for related_entity in json_response['data']['relatedEntities']: + related_type = related_entity['type'] + if related_type in self.related_attribute_types: + # Related entities returned as additional attributes + for related in related_entity['entities']: + if int(related["count"]) > 4: + indicator = related['entity']['name'] + self.add_related_attribute(indicator, related_type) + elif related_type in self.galaxy_tag_types: + # Related entities added as galaxy-tags to the enriched attribute + galaxy_tags = [] + for related in related_entity['entities']: + if int(related["count"]) > 4: + indicator = related['entity']['name'] + galaxy = self.galaxy_finder.find_galaxy_match(indicator, related_type) + # Handle deduplication of galaxy tags + if galaxy and galaxy not in galaxy_tags: + galaxy_tags.append(galaxy) + for galaxy in galaxy_tags: + self.add_tag(galaxy) + except KeyError as error: + misperrors['error'] = 'Unexpected format in Recorded Future api response.' + raise error + + def add_related_attribute(self, indicator: str, related_type: str) -> None: + """Helper method for adding an indicator to the related attribute list.""" + out_type = self.get_output_type(related_type, indicator) + attribute = MISPAttribute() + attribute.from_dict(**{'value': indicator, 'type': out_type, 'distribution': 0}) + self.related_attributes.append((related_type, attribute)) + + def add_tag(self, tag_name: str, hex_color: str = None) -> None: + """Helper method for adding a tag to the enriched attribute.""" + tag = MISPTag() + tag_properties = {'name': tag_name} + if hex_color: + tag_properties['colour'] = hex_color + tag.from_dict(**tag_properties) + self.enriched_attribute.add_tag(tag) + + def get_output_type(self, related_type: str, indicator: str) -> str: + """Helper method for translating a Recorded Future related type to a MISP output type.""" + output_type = 'text' + if related_type == 'RelatedIpAddress': + output_type = 'ip-dst' + elif related_type == 'RelatedInternetDomainName': + output_type = 'domain' + elif related_type == 'RelatedHash': + hash_len = len(indicator) + if hash_len == 64: + output_type = 'sha256' + elif hash_len == 40: + output_type = 'sha1' + elif hash_len == 32: + output_type = 'md5' + elif related_type == 'RelatedEmailAddress': + output_type = 'email-src' + elif related_type == 'RelatedCyberVulnerability': + signature = indicator.split('-')[0] + if signature == 'CVE': + output_type = 'vulnerability' + elif signature == 'CWE': + output_type = 'weakness' + return output_type + + def get_results(self) -> dict: + """Build and return the enrichment results.""" + self.enrichment_object.add_attribute('Enriched attribute', **self.enriched_attribute) + for related_type, attribute in self.related_attributes: + self.enrichment_object.add_attribute(related_type, **attribute) + self.event.add_object(**self.enrichment_object) + event = json.loads(self.event.to_json()) + result = {key: event[key] for key in ['Object'] if key in event} + return {'results': result} + + +def handler(q=False): + """Handle enrichment.""" + if q is False: + return False + request = json.loads(q) + + if request.get('config') and request['config'].get('token'): + token = request['config'].get('token') + else: + misperrors['error'] = 'Missing Recorded Future token.' + return misperrors + + input_attribute = request.get('attribute') + rf_enricher = RFEnricher(token, input_attribute) + try: + rf_enricher.enrich() + except (requests.HTTPError, KeyError): + return misperrors + + return rf_enricher.get_results() + + +def introspection(): + """Returns a dict of the supported attributes.""" + return mispattributes + + +def version(): + """Returns a dict with the version and the associated meta-data + including potential configurations required of the module.""" + moduleinfo['config'] = moduleconfig + return moduleinfo From 8180ecbfa80a9cac3fd5ff9601b00ff913fdc98b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 27 Jul 2020 17:20:36 +0200 Subject: [PATCH 161/220] chg: Making use of the Greynoise v2 API --- misp_modules/modules/expansion/greynoise.py | 56 +++++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/misp_modules/modules/expansion/greynoise.py b/misp_modules/modules/expansion/greynoise.py index dd54158..4cd89d5 100644 --- a/misp_modules/modules/expansion/greynoise.py +++ b/misp_modules/modules/expansion/greynoise.py @@ -3,35 +3,59 @@ import json misperrors = {'error': 'Error'} mispattributes = {'input': ['ip-dst', 'ip-src'], 'output': ['text']} -moduleinfo = {'version': '0.1', 'author': 'Aurélien Schwab ', 'description': 'Module to access GreyNoise.io API.', 'module-type': ['hover']} -moduleconfig = ['user-agent'] # TODO take this into account in the code +moduleinfo = { + 'version': '0.2', + 'author': 'Aurélien Schwab ', + 'description': 'Module to access GreyNoise.io API.', + 'module-type': ['hover'] +} +moduleconfig = ['api_key'] -greynoise_api_url = 'http://api.greynoise.io:8888/v1/query/ip' -default_user_agent = 'MISP-Module' +greynoise_api_url = 'https://api.greynoise.io/v2/noise/quick/' +codes_mapping = { + '0x00': 'The IP has never been observed scanning the Internet', + '0x01': 'The IP has been observed by the GreyNoise sensor network', + '0x02': 'The IP has been observed scanning the GreyNoise sensor network, but has not completed a full connection, meaning this can be spoofed', + '0x03': 'The IP is adjacent to another host that has been directly observed by the GreyNoise sensor network', + '0x04': 'Reserved', + '0x05': 'This IP is commonly spoofed in Internet-scan activity', + '0x06': 'This IP has been observed as noise, but this host belongs to a cloud provider where IPs can be cycled frequently', + '0x07': 'This IP is invalid', + '0x08': 'This IP was classified as noise, but has not been observed engaging in Internet-wide scans or attacks in over 60 days' +} def handler(q=False): if q is False: return False request = json.loads(q) + if not request.get('config') or not request['config'].get('api_key'): + return {'error': 'Missing Greynoise API key.'} + headers = { + 'Accept': 'application/json', + 'key': request['config']['api_key'] + } for input_type in mispattributes['input']: if input_type in request: ip = request[input_type] break else: - misperrors['error'] = "Unsupported attributes type" + misperrors['error'] = "Unsupported attributes type." return misperrors - data = {'ip': ip} - r = requests.post(greynoise_api_url, data=data, headers={'user-agent': default_user_agent}) # Real request - if r.status_code == 200: # OK (record found) - response = r.text - if response: - return {'results': [{'types': mispattributes['output'], 'values': response}]} - elif r.status_code == 404: # Not found (not an error) - return {'results': [{'types': mispattributes['output'], 'values': 'No data'}]} - else: # Real error - misperrors['error'] = 'GreyNoise API not accessible (HTTP ' + str(r.status_code) + ')' - return misperrors['error'] + response = requests.get(f'{greynoise_api_url}{ip}', headers=headers) # Real request + if response.status_code == 200: # OK (record found) + return {'results': [{'types': mispattributes['output'], 'values': codes_mapping[response.json()['code']]}]} + # There is an error + errors = { + 400: "Bad request.", + 401: "Unauthorized. Please check your API key.", + 429: "Too many requests. You've hit the rate-limit." + } + try: + misperrors['error'] = errors[response.status_code] + except KeyError: + misperrors['error'] = f'GreyNoise API not accessible (HTTP {response.status_code})' + return misperrors['error'] def introspection(): From f7b60bed2982454e922d4e23a33b033413b3dfc3 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 27 Jul 2020 17:21:52 +0200 Subject: [PATCH 162/220] chg: Updated Greynoise tests following the latest changes on the expansion module --- tests/test_expansions.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index b853c25..a56fbe7 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -229,11 +229,24 @@ class TestExpansions(unittest.TestCase): self.assertEqual(to_check, 'OK (Not Found)', response) def test_greynoise(self): - query = {"module": "greynoise", "ip-dst": "1.1.1.1"} - response = self.misp_modules_post(query) - value = self.get_values(response) - if value != 'GreyNoise API not accessible (HTTP 429)': - self.assertTrue(value.startswith('{"ip":"1.1.1.1","status":"ok"')) + module_name = 'greynoise' + query = {"module": module_name, "ip-dst": "1.1.1.1"} + if module_name in self.configs: + query['config'] = self.configs[module_name] + response = self.misp_modules_post(query) + try: + self.assertEqual(self.get_values(response), 'This IP is commonly spoofed in Internet-scan activity') + except Exception: + self.assertIn( + self.get_errors(reponse), + ( + "Unauthorized. Please check your API key.", + "Too many requests. You've hit the rate-limit." + ) + ) + else: + response = self.misp_modules_post(query) + self.assertEqual(self.get_errors(response), 'Missing Greynoise API key.') def test_ipasn(self): query = {"module": "ipasn", From 6d528628c7f7aa65ec2e25a1074abc52a0818fab Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 27 Jul 2020 17:26:07 +0200 Subject: [PATCH 163/220] chg: Updated documentation about the greynoise module --- doc/README.md | 4 +++- doc/expansion/greynoise.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/README.md b/doc/README.md index e173ad4..21030d4 100644 --- a/doc/README.md +++ b/doc/README.md @@ -502,13 +502,15 @@ Module to query a local copy of Maxmind's Geolite database. Module to access GreyNoise.io API - **features**: ->The module takes an IP address as input and queries Greynoise for some additional information about it. The result is returned as text. +>The module takes an IP address as input and queries Greynoise for some additional information about it: basically it checks whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. The result is returned as text. - **input**: >An IP address. - **output**: >Additional information about the IP fetched from Greynoise API. - **references**: >https://greynoise.io/, https://github.com/GreyNoise-Intelligence/api.greynoise.io +- **requirements**: +>A Greynoise API key. ----- diff --git a/doc/expansion/greynoise.json b/doc/expansion/greynoise.json index f1f1003..49ba481 100644 --- a/doc/expansion/greynoise.json +++ b/doc/expansion/greynoise.json @@ -1,9 +1,9 @@ { "description": "Module to access GreyNoise.io API", "logo": "logos/greynoise.png", - "requirements": [], + "requirements": ["A Greynoise API key."], "input": "An IP address.", "output": "Additional information about the IP fetched from Greynoise API.", "references": ["https://greynoise.io/", "https://github.com/GreyNoise-Intelligence/api.greynoise.io"], - "features": "The module takes an IP address as input and queries Greynoise for some additional information about it. The result is returned as text." + "features": "The module takes an IP address as input and queries Greynoise for some additional information about it: basically it checks whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. The result is returned as text." } From 3b7a5c4dc2541f3b07baee69a7e8b9694a1627fc Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 28 Jul 2020 11:47:53 +0200 Subject: [PATCH 164/220] add: Specific error message for misp_standard format expansion modules - Checking if the input format is respected and displaying an error message if it is not --- misp_modules/modules/expansion/__init__.py | 9 +++++++++ misp_modules/modules/expansion/apivoid.py | 7 ++++++- misp_modules/modules/expansion/assemblyline_query.py | 5 +++++ misp_modules/modules/expansion/censys_enrich.py | 7 ++++--- misp_modules/modules/expansion/circl_passivedns.py | 7 ++++--- misp_modules/modules/expansion/circl_passivessl.py | 7 ++++--- misp_modules/modules/expansion/cve_advanced.py | 8 +++++--- misp_modules/modules/expansion/cytomic_orion.py | 6 ++++-- misp_modules/modules/expansion/ipasn.py | 11 ++++++----- misp_modules/modules/expansion/joesandbox_query.py | 5 +++++ misp_modules/modules/expansion/lastline_query.py | 4 +++- misp_modules/modules/expansion/malwarebazaar.py | 5 +++++ misp_modules/modules/expansion/ransomcoindb.py | 5 +++++ misp_modules/modules/expansion/recordedfuture.py | 5 +++++ misp_modules/modules/expansion/sophoslabs_intelix.py | 7 +++++++ misp_modules/modules/expansion/trustar_enrich.py | 5 +++++ misp_modules/modules/expansion/urlhaus.py | 8 +++++++- misp_modules/modules/expansion/virustotal.py | 8 +++++++- misp_modules/modules/expansion/virustotal_public.py | 7 ++++++- misp_modules/modules/expansion/xforceexchange.py | 5 +++++ 20 files changed, 107 insertions(+), 24 deletions(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 14d5499..af895e3 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -19,3 +19,12 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture'] + + +minimum_required_fields = ('type', 'uuid', 'value') + +checking_error = 'containing at least a "type" field and a "value" field' +standard_error_message = 'This module requires an "attribute" field as input' + +def check_input_attribute(attribute, requirements=minimum_required_fields): + return all(feature in attribute for feature in requirements) diff --git a/misp_modules/modules/expansion/apivoid.py b/misp_modules/modules/expansion/apivoid.py index 5d6395e..a71b5e6 100755 --- a/misp_modules/modules/expansion/apivoid.py +++ b/misp_modules/modules/expansion/apivoid.py @@ -1,5 +1,6 @@ import json import requests +from . import check_input_attribute, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} @@ -74,7 +75,11 @@ def handler(q=False): request = json.loads(q) if not request.get('config', {}).get('apikey'): return {'error': 'An API key for APIVoid is required.'} - attribute = request.get('attribute') + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} apikey = request['config']['apikey'] apivoid_parser = APIVoidParser(attribute) apivoid_parser.parse_domain(apikey) diff --git a/misp_modules/modules/expansion/assemblyline_query.py b/misp_modules/modules/expansion/assemblyline_query.py index 226e4dd..67fce45 100644 --- a/misp_modules/modules/expansion/assemblyline_query.py +++ b/misp_modules/modules/expansion/assemblyline_query.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import json +from . import check_input_attribute, standard_error_message from assemblyline_client import Client, ClientError from collections import defaultdict from pymisp import MISPAttribute, MISPEvent, MISPObject @@ -139,6 +140,10 @@ def handler(q=False): if q is False: return False request = json.loads(q) + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + if request['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} if not request.get('config'): return {"error": "Missing configuration."} if not request['config'].get('apiurl'): diff --git a/misp_modules/modules/expansion/censys_enrich.py b/misp_modules/modules/expansion/censys_enrich.py index 0fc61ae..d5823ff 100644 --- a/misp_modules/modules/expansion/censys_enrich.py +++ b/misp_modules/modules/expansion/censys_enrich.py @@ -3,6 +3,7 @@ import json import base64 import codecs from dateutil.parser import isoparse +from . import check_input_attribute, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject try: import censys.base @@ -36,11 +37,11 @@ def handler(q=False): api_id = request['config']['api_id'] api_secret = request['config']['api_secret'] - if not request.get('attribute'): - return {'error': 'Unsupported input.'} + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} attribute = request['attribute'] if not any(input_type == attribute['type'] for input_type in mispattributes['input']): - return {'error': 'Unsupported attributes type'} + return {'error': 'Unsupported attribute type.'} attribute = MISPAttribute() attribute.from_dict(**request['attribute']) diff --git a/misp_modules/modules/expansion/circl_passivedns.py b/misp_modules/modules/expansion/circl_passivedns.py index d278a85..5f98314 100755 --- a/misp_modules/modules/expansion/circl_passivedns.py +++ b/misp_modules/modules/expansion/circl_passivedns.py @@ -1,5 +1,6 @@ import json import pypdns +from . import check_input_attribute, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'], 'format': 'misp_standard'} @@ -58,11 +59,11 @@ def handler(q=False): if not request['config'].get('username') or not request['config'].get('password'): return {'error': 'CIRCL Passive DNS authentication is incomplete, please provide your username and password.'} authentication = (request['config']['username'], request['config']['password']) - if not request.get('attribute'): - return {'error': 'Unsupported input.'} + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} attribute = request['attribute'] if not any(input_type == attribute['type'] for input_type in mispattributes['input']): - return {'error': 'Unsupported attributes type'} + return {'error': 'Unsupported attribute type.'} pdns_parser = PassiveDNSParser(attribute, authentication) pdns_parser.parse() return pdns_parser.get_results() diff --git a/misp_modules/modules/expansion/circl_passivessl.py b/misp_modules/modules/expansion/circl_passivessl.py index 102bed8..65783d7 100755 --- a/misp_modules/modules/expansion/circl_passivessl.py +++ b/misp_modules/modules/expansion/circl_passivessl.py @@ -1,5 +1,6 @@ import json import pypssl +from . import check_input_attribute, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject mispattributes = {'input': ['ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'], 'format': 'misp_standard'} @@ -83,11 +84,11 @@ def handler(q=False): if not request['config'].get('username') or not request['config'].get('password'): return {'error': 'CIRCL Passive SSL authentication is incomplete, please provide your username and password.'} authentication = (request['config']['username'], request['config']['password']) - if not request.get('attribute'): - return {'error': 'Unsupported input.'} + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} attribute = request['attribute'] if not any(input_type == attribute['type'] for input_type in mispattributes['input']): - return {'error': 'Unsupported attributes type'} + return {'error': 'Unsupported attribute type.'} pssl_parser = PassiveSSLParser(attribute, authentication) pssl_parser.parse() return pssl_parser.get_results() diff --git a/misp_modules/modules/expansion/cve_advanced.py b/misp_modules/modules/expansion/cve_advanced.py index 86cba8c..bd2d277 100644 --- a/misp_modules/modules/expansion/cve_advanced.py +++ b/misp_modules/modules/expansion/cve_advanced.py @@ -1,7 +1,8 @@ -from collections import defaultdict -from pymisp import MISPEvent, MISPObject import json import requests +from . import check_input_attribute, standard_error_message +from collections import defaultdict +from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'} @@ -108,7 +109,8 @@ def handler(q=False): if q is False: return False request = json.loads(q) - attribute = request.get('attribute') + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} if attribute.get('type') != 'vulnerability': misperrors['error'] = 'Vulnerability id missing.' return misperrors diff --git a/misp_modules/modules/expansion/cytomic_orion.py b/misp_modules/modules/expansion/cytomic_orion.py index 9723ed6..b730135 100755 --- a/misp_modules/modules/expansion/cytomic_orion.py +++ b/misp_modules/modules/expansion/cytomic_orion.py @@ -7,6 +7,7 @@ An expansion module to enrich attributes in MISP and share indicators of comprom ''' +from . import check_input_attribute, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject import json import requests @@ -146,9 +147,10 @@ def handler(q=False): if not request.get('attribute'): return {'error': 'Unsupported input.'} - attribute = request['attribute'] + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} if not any(input_type == attribute['type'] for input_type in mispattributes['input']): - return {'error': 'Unsupported attributes type'} + return {'error': 'Unsupported attribute type.'} if not request.get('config'): return {'error': 'Missing configuration'} diff --git a/misp_modules/modules/expansion/ipasn.py b/misp_modules/modules/expansion/ipasn.py index 3c6867c..3a32358 100755 --- a/misp_modules/modules/expansion/ipasn.py +++ b/misp_modules/modules/expansion/ipasn.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json +from . import check_input_attribute, standard_error_message from pyipasnhistory import IPASNHistory from pymisp import MISPAttribute, MISPEvent, MISPObject @@ -34,11 +35,11 @@ def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('attribute') and request['attribute'].get('type') in mispattributes['input']: - toquery = request['attribute']['value'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + if request['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} + toquery = request['attribute']['value'] ipasn = IPASNHistory() values = ipasn.query(toquery) diff --git a/misp_modules/modules/expansion/joesandbox_query.py b/misp_modules/modules/expansion/joesandbox_query.py index 1ace259..b9c4987 100644 --- a/misp_modules/modules/expansion/joesandbox_query.py +++ b/misp_modules/modules/expansion/joesandbox_query.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import jbxapi import json +from . import check_input_attribute, checking_error, standard_error_message from joe_parser import JoeParser misperrors = {'error': 'Error'} @@ -27,6 +28,10 @@ def handler(q=False): if not apikey: return {'error': 'No API key provided'} + if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')): + return {'error': f'{standard_error_message}, {checking_error} that is the link to the Joe Sandbox report.'} + if request['attribute']['type'] != 'link': + return {'error': 'Unsupported attribute type.'} url = request['attribute']['value'] if "/submissions/" not in url: return {'error': "The URL does not point to a Joe Sandbox analysis."} diff --git a/misp_modules/modules/expansion/lastline_query.py b/misp_modules/modules/expansion/lastline_query.py index 4ce4e47..dcabda5 100644 --- a/misp_modules/modules/expansion/lastline_query.py +++ b/misp_modules/modules/expansion/lastline_query.py @@ -3,8 +3,8 @@ Module (type "expansion") to query a Lastline report from an analysis link. """ import json - import lastline_api +from . import check_input_attribute, checking_error, standard_error_message misperrors = { @@ -52,6 +52,8 @@ def handler(q=False): try: config = request["config"] auth_data = lastline_api.LastlineAbstractClient.get_login_params_from_dict(config) + if not request.get('attribute') or not request['attribute'].get('value'): + return {'error': f'{standard_error_message}, {checking_error} that is the link to a Lastline analysis.'} analysis_link = request['attribute']['value'] # The API url changes based on the analysis link host name api_url = lastline_api.get_portal_url_from_task_link(analysis_link) diff --git a/misp_modules/modules/expansion/malwarebazaar.py b/misp_modules/modules/expansion/malwarebazaar.py index 4574b75..60739e8 100644 --- a/misp_modules/modules/expansion/malwarebazaar.py +++ b/misp_modules/modules/expansion/malwarebazaar.py @@ -1,5 +1,6 @@ import json import requests +from . import check_input_attribute, checking_error, standard_error_message from pymisp import MISPEvent, MISPObject mispattributes = {'input': ['md5', 'sha1', 'sha256'], @@ -34,7 +35,11 @@ def handler(q=False): if q is False: return False request = json.loads(q) + if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')): + return {'error': f'{standard_error_message}, {checking_error} that is the hash to submit to Malware Bazaar.'} attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} url = 'https://mb-api.abuse.ch/api/v1/' response = requests.post(url, data={'query': 'get_info', 'hash': attribute['value']}).json() query_status = response['query_status'] diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index 2b9b566..d9a1712 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -1,4 +1,5 @@ import json +from . import check_input_attribute, checking_error, standard_error_message from ._ransomcoindb import ransomcoindb from pymisp import MISPObject @@ -28,6 +29,10 @@ def handler(q=False): q = json.loads(q) if "config" not in q or "api-key" not in q["config"]: return {"error": "Ransomcoindb API key is missing"} + if not q.get('attribute') or not check_input_attribute(attribute, requirements=('type', 'value')): + return {'error': f'{standard_error_message}, {checking_error}.'} + if q['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} api_key = q["config"]["api-key"] r = {"results": []} diff --git a/misp_modules/modules/expansion/recordedfuture.py b/misp_modules/modules/expansion/recordedfuture.py index c42a42b..2f71dbb 100644 --- a/misp_modules/modules/expansion/recordedfuture.py +++ b/misp_modules/modules/expansion/recordedfuture.py @@ -1,6 +1,7 @@ import json import logging import requests +from . import check_input_attribute, checking_error, standard_error_message from urllib.parse import quote from pymisp import MISPAttribute, MISPEvent, MISPTag, MISPObject @@ -257,6 +258,10 @@ def handler(q=False): else: misperrors['error'] = 'Missing Recorded Future token.' return misperrors + if not request.get('attribute') or not check_input_attribute(request['atttribute'], requirements=('type', 'value')): + return {'error': f'{standard_error_message}, {checking_error}.'} + if request['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} input_attribute = request.get('attribute') rf_enricher = RFEnricher(token, input_attribute) diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 017683a..6c6204a 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -1,3 +1,4 @@ +from. import check_input_attribute, checking_error, standard_error_message from pymisp import MISPEvent, MISPObject import json import requests @@ -105,6 +106,12 @@ def handler(q=False): misperrors['error'] = "Missing client_id or client_secret value for SOPHOSLabs Intelix. \ It's free to sign up here https://aws.amazon.com/marketplace/pp/B07SLZPMCS." return misperrors + to_check = (('type', 'value'), ('type', 'value1')) + if not request.get('attribute') or not any(check_input_attribute(request['attribute'], requirements=check) for check in to_check): + return {'error': f'{standard_error_message}, {checking_error}.'} + attribute = request['attribute'] + if attribute['type'] not in misp_types_in: + return {'error': 'Unsupported attribute type.'} client = SophosLabsApi(j['config']['client_id'], j['config']['client_secret']) if j['attribute']['type'] == "sha256": client.hash_lookup(j['attribute']['value1']) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index efe7c53..a0d6177 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -1,5 +1,6 @@ import json import pymisp +from . import check_input_attribute, checking_error, standard_error_message from pymisp import MISPAttribute, MISPEvent, MISPObject from trustar import TruStar @@ -110,7 +111,11 @@ def handler(q=False): misperrors['error'] = "Your TruSTAR API key and secret are required for indicator enrichment." return misperrors + if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')): + return {'error': f'{standard_error_message}, {checking_error}.'} attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} trustar_parser = TruSTARParser(attribute, config) try: diff --git a/misp_modules/modules/expansion/urlhaus.py b/misp_modules/modules/expansion/urlhaus.py index baaaaf6..ed13b77 100644 --- a/misp_modules/modules/expansion/urlhaus.py +++ b/misp_modules/modules/expansion/urlhaus.py @@ -1,6 +1,8 @@ -from pymisp import MISPAttribute, MISPEvent, MISPObject +# -*- coding: utf-8 -*- import json import requests +from . import check_input_attribute, standard_error_message +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['domain', 'hostname', 'ip-src', 'ip-dst', 'md5', 'sha256', 'url'], @@ -134,7 +136,11 @@ def handler(q=False): if q is False: return False request = json.loads(q) + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} urlhaus_parser = _misp_type_mapping[attribute['type']](attribute) return urlhaus_parser.query_api() diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index b09de81..12f7552 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -1,6 +1,7 @@ -from pymisp import MISPAttribute, MISPEvent, MISPObject import json import requests +from . import check_input_attribute, standard_error_message +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "url"], @@ -195,6 +196,11 @@ def handler(q=False): if not request.get('config') or not request['config'].get('apikey'): misperrors['error'] = "A VirusTotal api key is required for this module." return misperrors + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + if request['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} + event_limit = request['config'].get('event_limit') if not isinstance(event_limit, int): event_limit = 5 diff --git a/misp_modules/modules/expansion/virustotal_public.py b/misp_modules/modules/expansion/virustotal_public.py index e7c2e96..6ffb7f9 100644 --- a/misp_modules/modules/expansion/virustotal_public.py +++ b/misp_modules/modules/expansion/virustotal_public.py @@ -1,6 +1,7 @@ -from pymisp import MISPAttribute, MISPEvent, MISPObject import json import requests +from . import check_input_attribute, standard_error_message +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['hostname', 'domain', "ip-src", "ip-dst", "md5", "sha1", "sha256", "url"], @@ -174,7 +175,11 @@ def handler(q=False): if not request.get('config') or not request['config'].get('apikey'): misperrors['error'] = "A VirusTotal api key is required for this module." return misperrors + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} query_type, to_call = misp_type_mapping[attribute['type']] parser = to_call(request['config']['apikey'], attribute) query_result = parser.get_query_result(query_type) diff --git a/misp_modules/modules/expansion/xforceexchange.py b/misp_modules/modules/expansion/xforceexchange.py index 7999ce2..936917f 100644 --- a/misp_modules/modules/expansion/xforceexchange.py +++ b/misp_modules/modules/expansion/xforceexchange.py @@ -1,6 +1,7 @@ import requests import json import sys +from . import check_input_attribute, standard_error_message from collections import defaultdict from pymisp import MISPAttribute, MISPEvent, MISPObject from requests.auth import HTTPBasicAuth @@ -160,6 +161,10 @@ def handler(q=False): return misperrors key = request["config"]["apikey"] password = request['config']['apipassword'] + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message} which should contain at least a type, a value and an uuid.'} + if request['attribute']['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} parser = XforceExchange(request['attribute'], key, password) parser.parse() return parser.get_result() From 3ab67b23b66831dc49e6ecd0a346a8b9b52cbdce Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 28 Jul 2020 11:56:03 +0200 Subject: [PATCH 165/220] fix: Avoid issues with the attribute value field name - The module setup allows 'value1' as attribute value field name, but we want to make sure that users passing standard misp format with 'value' instead, will not have issues, as well as keeping the current setup --- .../modules/expansion/sophoslabs_intelix.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 6c6204a..38d4293 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -113,12 +113,18 @@ def handler(q=False): if attribute['type'] not in misp_types_in: return {'error': 'Unsupported attribute type.'} client = SophosLabsApi(j['config']['client_id'], j['config']['client_secret']) - if j['attribute']['type'] == "sha256": - client.hash_lookup(j['attribute']['value1']) - if j['attribute']['type'] in ['ip-dst', 'ip-src', 'ip']: - client.ip_lookup(j["attribute"]["value1"]) - if j['attribute']['type'] in ['uri', 'url', 'domain', 'hostname']: - client.url_lookup(j["attribute"]["value1"]) + mapping = { + 'sha256': 'hash_lookup', + 'ip-dst': 'ip_lookup', + 'ip-src': 'ip_lookup', + 'ip': 'ip_lookup', + 'uri': 'url_lookup', + 'url': 'url_lookup', + 'domain': 'url_lookup', + 'hostname': 'url_lookup' + } + attribute_value = attribute['value'] if 'value' in attribute else attribute['value1'] + getattr(client, mapping[attribute['type']])(attribute_value) return client.get_result() From a316e1877f55995df55ad917ca8f8a3a471548e2 Mon Sep 17 00:00:00 2001 From: johannesh Date: Tue, 28 Jul 2020 13:33:48 +0200 Subject: [PATCH 166/220] Add Recorded Future module documentation --- doc/README.md | 18 ++++++++++++++++++ doc/expansion/recordedfuture.json | 9 +++++++++ doc/logos/recordedfuture.png | Bin 0 -> 39310 bytes 3 files changed, 27 insertions(+) create mode 100644 doc/expansion/recordedfuture.json create mode 100644 doc/logos/recordedfuture.png diff --git a/doc/README.md b/doc/README.md index e173ad4..77c1e82 100644 --- a/doc/README.md +++ b/doc/README.md @@ -964,6 +964,24 @@ Module to check an IPv4 address against known RBLs. ----- +#### [recordedfuture](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/recordedfuture.py) + + + +Module to enrich attributes with threat intelligence from Recorded Future. +- **features**: +>Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future will be matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object will also include a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes. +- **input**: +>A MISP attribute of one of the following types: ip, ip-src, ip-dst, domain, hostname, md5, sha1, sha256, uri, url, vulnerability, weakness. +- **output**: +>A MISP object containing a copy of the enriched attribute with added tags from Recorded Future and a list of new attributes related to the enriched attribute. +- **references**: +>https://www.recordedfuture.com/ +- **requirements**: +>A Recorded Future API token. + +----- + #### [reversedns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/reversedns.py) Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes. diff --git a/doc/expansion/recordedfuture.json b/doc/expansion/recordedfuture.json new file mode 100644 index 0000000..bbeea07 --- /dev/null +++ b/doc/expansion/recordedfuture.json @@ -0,0 +1,9 @@ +{ + "description": "Module to enrich attributes with threat intelligence from Recorded Future.", + "logo": "logos/recordedfuture.png", + "requirements": ["A Recorded Future API token."], + "input": "A MISP attribute of one of the following types: ip, ip-src, ip-dst, domain, hostname, md5, sha1, sha256, uri, url, vulnerability, weakness.", + "output": "A MISP object containing a copy of the enriched attribute with added tags from Recorded Future and a list of new attributes related to the enriched attribute.", + "references": ["https://www.recordedfuture.com/"], + "features": "Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future will be matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object will also include a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes." +} diff --git a/doc/logos/recordedfuture.png b/doc/logos/recordedfuture.png new file mode 100644 index 0000000000000000000000000000000000000000..a208c0453e811fe2f8fefab566158f5d4ca76e9a GIT binary patch literal 39310 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 literal 0 HcmV?d00001 From 988bf3487d4c47b16f9d95d42d85b6158ed4f1a9 Mon Sep 17 00:00:00 2001 From: johannesh Date: Tue, 28 Jul 2020 13:46:43 +0200 Subject: [PATCH 167/220] Improve wording --- doc/README.md | 2 +- doc/expansion/recordedfuture.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.md b/doc/README.md index 77c1e82..5e15147 100644 --- a/doc/README.md +++ b/doc/README.md @@ -970,7 +970,7 @@ Module to check an IPv4 address against known RBLs. Module to enrich attributes with threat intelligence from Recorded Future. - **features**: ->Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future will be matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object will also include a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes. +>Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future is matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object also includes a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes. - **input**: >A MISP attribute of one of the following types: ip, ip-src, ip-dst, domain, hostname, md5, sha1, sha256, uri, url, vulnerability, weakness. - **output**: diff --git a/doc/expansion/recordedfuture.json b/doc/expansion/recordedfuture.json index bbeea07..2fec7eb 100644 --- a/doc/expansion/recordedfuture.json +++ b/doc/expansion/recordedfuture.json @@ -5,5 +5,5 @@ "input": "A MISP attribute of one of the following types: ip, ip-src, ip-dst, domain, hostname, md5, sha1, sha256, uri, url, vulnerability, weakness.", "output": "A MISP object containing a copy of the enriched attribute with added tags from Recorded Future and a list of new attributes related to the enriched attribute.", "references": ["https://www.recordedfuture.com/"], - "features": "Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future will be matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object will also include a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes." + "features": "Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future is matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object also includes a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes." } From d2661c7a2017f669baf98cfaee5f4b9a7a18bf66 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 28 Jul 2020 15:06:25 +0200 Subject: [PATCH 168/220] fix: Fixed pep8 + some copy paste issues introduced with the latest commits --- misp_modules/modules/expansion/__init__.py | 1 + misp_modules/modules/expansion/cve_advanced.py | 1 + misp_modules/modules/expansion/cytomic_orion.py | 1 + misp_modules/modules/expansion/ransomcoindb.py | 2 +- misp_modules/modules/expansion/sophoslabs_intelix.py | 8 ++++---- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index af895e3..1b6d2bb 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -26,5 +26,6 @@ minimum_required_fields = ('type', 'uuid', 'value') checking_error = 'containing at least a "type" field and a "value" field' standard_error_message = 'This module requires an "attribute" field as input' + def check_input_attribute(attribute, requirements=minimum_required_fields): return all(feature in attribute for feature in requirements) diff --git a/misp_modules/modules/expansion/cve_advanced.py b/misp_modules/modules/expansion/cve_advanced.py index bd2d277..cd36655 100644 --- a/misp_modules/modules/expansion/cve_advanced.py +++ b/misp_modules/modules/expansion/cve_advanced.py @@ -111,6 +111,7 @@ def handler(q=False): request = json.loads(q) if not request.get('attribute') or not check_input_attribute(request['attribute']): return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] if attribute.get('type') != 'vulnerability': misperrors['error'] = 'Vulnerability id missing.' return misperrors diff --git a/misp_modules/modules/expansion/cytomic_orion.py b/misp_modules/modules/expansion/cytomic_orion.py index b730135..c13b254 100755 --- a/misp_modules/modules/expansion/cytomic_orion.py +++ b/misp_modules/modules/expansion/cytomic_orion.py @@ -149,6 +149,7 @@ def handler(q=False): if not request.get('attribute') or not check_input_attribute(request['attribute']): return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] if not any(input_type == attribute['type'] for input_type in mispattributes['input']): return {'error': 'Unsupported attribute type.'} diff --git a/misp_modules/modules/expansion/ransomcoindb.py b/misp_modules/modules/expansion/ransomcoindb.py index d9a1712..0e05855 100644 --- a/misp_modules/modules/expansion/ransomcoindb.py +++ b/misp_modules/modules/expansion/ransomcoindb.py @@ -29,7 +29,7 @@ def handler(q=False): q = json.loads(q) if "config" not in q or "api-key" not in q["config"]: return {"error": "Ransomcoindb API key is missing"} - if not q.get('attribute') or not check_input_attribute(attribute, requirements=('type', 'value')): + if not q.get('attribute') or not check_input_attribute(q['attribute'], requirements=('type', 'value')): return {'error': f'{standard_error_message}, {checking_error}.'} if q['attribute']['type'] not in mispattributes['input']: return {'error': 'Unsupported attribute type.'} diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 38d4293..254437b 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -1,8 +1,8 @@ -from. import check_input_attribute, checking_error, standard_error_message -from pymisp import MISPEvent, MISPObject import json import requests import base64 +from. import check_input_attribute, checking_error, standard_error_message +from pymisp import MISPEvent, MISPObject from urllib.parse import quote moduleinfo = {'version': '1.0', @@ -107,9 +107,9 @@ def handler(q=False): It's free to sign up here https://aws.amazon.com/marketplace/pp/B07SLZPMCS." return misperrors to_check = (('type', 'value'), ('type', 'value1')) - if not request.get('attribute') or not any(check_input_attribute(request['attribute'], requirements=check) for check in to_check): + if not j.get('attribute') or not any(check_input_attribute(j['attribute'], requirements=check) for check in to_check): return {'error': f'{standard_error_message}, {checking_error}.'} - attribute = request['attribute'] + attribute = j['attribute'] if attribute['type'] not in misp_types_in: return {'error': 'Unsupported attribute type.'} client = SophosLabsApi(j['config']['client_id'], j['config']['client_secret']) From f1dac0c8dfb5aff60611d3dbd439b67feaa1a03e Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 28 Jul 2020 15:23:24 +0200 Subject: [PATCH 169/220] fix: Fixed pep8 --- misp_modules/modules/expansion/lastline_query.py | 2 +- misp_modules/modules/expansion/sophoslabs_intelix.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/lastline_query.py b/misp_modules/modules/expansion/lastline_query.py index dcabda5..dbfdf14 100644 --- a/misp_modules/modules/expansion/lastline_query.py +++ b/misp_modules/modules/expansion/lastline_query.py @@ -52,7 +52,7 @@ def handler(q=False): try: config = request["config"] auth_data = lastline_api.LastlineAbstractClient.get_login_params_from_dict(config) - if not request.get('attribute') or not request['attribute'].get('value'): + if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')): return {'error': f'{standard_error_message}, {checking_error} that is the link to a Lastline analysis.'} analysis_link = request['attribute']['value'] # The API url changes based on the analysis link host name diff --git a/misp_modules/modules/expansion/sophoslabs_intelix.py b/misp_modules/modules/expansion/sophoslabs_intelix.py index 254437b..4d7c413 100644 --- a/misp_modules/modules/expansion/sophoslabs_intelix.py +++ b/misp_modules/modules/expansion/sophoslabs_intelix.py @@ -1,7 +1,7 @@ import json import requests import base64 -from. import check_input_attribute, checking_error, standard_error_message +from . import check_input_attribute, checking_error, standard_error_message from pymisp import MISPEvent, MISPObject from urllib.parse import quote From 0b869750d75dd170c502fca089ee989cf3a4f14c Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Wed, 29 Jul 2020 09:35:08 -0700 Subject: [PATCH 170/220] added description to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index db199a0..17d6f2b 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [STIX2 pattern syntax validator](misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax. * [ThreatCrowd](misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/). * [threatminer](misp_modules/modules/expansion/threatminer.py) - an expansion module to expand from [ThreatMiner](https://www.threatminer.org/). +* [TruSTAR Enrich](misp_modules/modules/expansion/trustar_enrich.py) - an expansion module to enrich MISP data with [TruSTAR](https://www.trustar.co/). * [urlhaus](misp_modules/modules/expansion/urlhaus.py) - Query urlhaus to get additional data about a domain, hash, hostname, ip or url. * [urlscan](misp_modules/modules/expansion/urlscan.py) - an expansion module to query [urlscan.io](https://urlscan.io). * [virustotal](misp_modules/modules/expansion/virustotal.py) - an expansion module to query the [VirusTotal](https://www.virustotal.com/gui/home) API with a high request rate limit required. (More details about the API: [here](https://developers.virustotal.com/reference)) From ee21a88127abc91332abfff10dabd04c1afca422 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Thu, 6 Aug 2020 21:59:13 -0700 Subject: [PATCH 171/220] updating to include metadata and alter type of trustar link generated --- .../modules/expansion/trustar_enrich.py | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 48b4895..f4df990 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -1,7 +1,10 @@ import json import pymisp +from base64 import b64encode +from collections import OrderedDict from pymisp import MISPAttribute, MISPEvent, MISPObject from trustar import TruStar +from urllib.parse import quote misperrors = {'error': "Error"} mispattributes = { @@ -33,9 +36,12 @@ class TruSTARParser: 'SHA256': "sha256" } + SUMMARY_FIELDS = ["source", "score", "attributes"] + METADATA_FIELDS = ["sightings", "first_seen", "last_seen", "tags"] + REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" - CLIENT_METATAG = "MISP-{}".format(pymisp.__version__) + CLIENT_METATAG = f"MISP-{pymisp.__version__}" def __init__(self, attribute, config): config['enclave_ids'] = config.get('enclave_ids', "").strip().split(',') @@ -55,20 +61,36 @@ class TruSTARParser: results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} return {'results': results} - def generate_trustar_links(self, entity_value): + def generate_trustar_link(self, entity_type, entity_value): """ - Generates links to TruSTAR reports if they exist. + Generates link to TruSTAR report of entity. :param entity_value: Value of entity. """ - report_links = list() - trustar_reports = self.ts_client.search_reports(entity_value) - for report in trustar_reports: - report_links.append(self.REPORT_BASE_URL.format(report.id)) + report_id = b64encode(quote(f"{entity_type}|{entity_value}").encode()).decode() - return report_links + return self.REPORT_BASE_URL.format(report_id) - def parse_indicator_summary(self, summaries): + def generate_enrichment_report(self, summary, metadata): + """ + Extracts desired fields from summary and metadata reports and + generates an enrichment report. + + :param summary: Indicator summary report. + :param metadata: Indicator metadata report. + :return: Enrichment report. + """ + enrichment_report = OrderedDict() + + for field in self.SUMMARY_FIELDS: + enrichment_report[field] = summary.get(field) + + for field in self.METADATA_FIELDS: + enrichment_report[field] = metadata.get(field) + + return enrichment_report + + def parse_indicator_summary(self, summaries, metadata): """ Converts a response from the TruSTAR /1.3/indicators/summaries endpoint a MISP trustar_report object and adds the summary data and links as attributes. @@ -78,18 +100,21 @@ class TruSTARParser: """ for summary in summaries: - trustar_obj = MISPObject('trustar_report') - indicator_type = summary.indicator_type - indicator_value = summary.value - if summary_type in self.ENTITY_TYPE_MAPPINGS: - trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], - value=indicator_value) - trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", - value=json.dumps(summary.to_dict(), sort_keys=True, indent=4)) - report_links = self.generate_trustar_links(indicator_value) - for link in report_links: - trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=link) - self.misp_event.add_object(**trustar_obj) + if summary.indicator_type in self.ENTITY_TYPE_MAPPINGS: + indicator_type = summary.indicator_type + indicator_value = summary.indicator_value + try: + enrichment_report = self.generate_enrichment_report(summary.to_dict(), metadata.to_dict()) + trustar_obj = MISPObject('trustar_report') + trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], + value=indicator_value) + trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", + value=json.dumps(enrichment_report, indent=4)) + report_link = self.generate_trustar_link(indicator_type, indicator_value) + trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=report_link) + self.misp_event.add_object(**trustar_obj) + except Exception as e: + misperrors['error'] = f"Error enriching data with TruSTAR -- {e}" def handler(q=False): @@ -114,10 +139,11 @@ def handler(q=False): trustar_parser = TruSTARParser(attribute, config) try: + metadata = trustar_parser.ts_client.get_indicators_metadata([attribute['value']]) summaries = list( trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE)) except Exception as e: - misperrors['error'] = "Unable to retrieve TruSTAR summary data: {}".format(e) + misperrors['error'] = f"Unable to retrieve TruSTAR summary data: {e}" return misperrors trustar_parser.parse_indicator_summary(summaries) From 85d319e85e1e53349685b3124aa13b020891b382 Mon Sep 17 00:00:00 2001 From: johannesh Date: Fri, 7 Aug 2020 10:36:40 +0200 Subject: [PATCH 172/220] Fix typo error introduced in commit: 3b7a5c4dc2541f3b07baee69a7e8b9694a1627fc --- misp_modules/modules/expansion/recordedfuture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/recordedfuture.py b/misp_modules/modules/expansion/recordedfuture.py index 2f71dbb..8fdff09 100644 --- a/misp_modules/modules/expansion/recordedfuture.py +++ b/misp_modules/modules/expansion/recordedfuture.py @@ -258,7 +258,7 @@ def handler(q=False): else: misperrors['error'] = 'Missing Recorded Future token.' return misperrors - if not request.get('attribute') or not check_input_attribute(request['atttribute'], requirements=('type', 'value')): + if not request.get('attribute') or not check_input_attribute(request['attribute'], requirements=('type', 'value')): return {'error': f'{standard_error_message}, {checking_error}.'} if request['attribute']['type'] not in mispattributes['input']: return {'error': 'Unsupported attribute type.'} From 2d464adfd635d89c9b9db402a17aece813a503ab Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sun, 9 Aug 2020 20:29:37 -0700 Subject: [PATCH 173/220] added error checking --- .../modules/expansion/trustar_enrich.py | 123 ++++++++++++------ 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index f4df990..81238ad 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -3,7 +3,7 @@ import pymisp from base64 import b64encode from collections import OrderedDict from pymisp import MISPAttribute, MISPEvent, MISPObject -from trustar import TruStar +from trustar import TruStar, Indicator from urllib.parse import quote misperrors = {'error': "Error"} @@ -36,7 +36,7 @@ class TruSTARParser: 'SHA256': "sha256" } - SUMMARY_FIELDS = ["source", "score", "attributes"] + SUMMARY_FIELDS = ["severityLevel", "source", "score", "attributes"] METADATA_FIELDS = ["sightings", "first_seen", "last_seen", "tags"] REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" @@ -57,64 +57,104 @@ class TruSTARParser: """ Returns the MISP Event enriched with TruSTAR indicator summary data. """ - event = json.loads(self.misp_event.to_json()) - results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} - return {'results': results} + try: + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + except Exception as e: + misperrors['error'] += f" -- Encountered issue serializing enrichment data -- {e}" + return misperrors def generate_trustar_link(self, entity_type, entity_value): """ Generates link to TruSTAR report of entity. + :param entity_type: Type of entity. :param entity_value: Value of entity. + :return: Link to indicator report in TruSTAR platform. """ report_id = b64encode(quote(f"{entity_type}|{entity_value}").encode()).decode() return self.REPORT_BASE_URL.format(report_id) + @staticmethod + def extract_tags(enrichment_report): + """ + Extracts tags from the enrichment report in order to add them + to the TruSTAR MISP Object. Removes tags from report to avoid + redundancy. + + :param: Enrichment data. + """ + if enrichment_report and enrichment_report.get('tags'): + return [tag.get('name') for tag in enrichment_report.pop('tags')] + return None + def generate_enrichment_report(self, summary, metadata): """ Extracts desired fields from summary and metadata reports and generates an enrichment report. - :param summary: Indicator summary report. - :param metadata: Indicator metadata report. + :param summary: Indicator summary report. + :param metadata: Indicator metadata report. :return: Enrichment report. """ enrichment_report = OrderedDict() - for field in self.SUMMARY_FIELDS: - enrichment_report[field] = summary.get(field) + if summary: + summary_dict = summary.to_dict() + enrichment_report.update( + {field: summary_dict[field] for field in self.SUMMARY_FIELDS if summary_dict.get(field)}) - for field in self.METADATA_FIELDS: - enrichment_report[field] = metadata.get(field) + if metadata: + metadata_dict = metadata.to_dict() + enrichment_report.update( + {field: metadata_dict[field] for field in self.METADATA_FIELDS if metadata_dict.get(field)}) return enrichment_report - def parse_indicator_summary(self, summaries, metadata): + def parse_indicator_summary(self, indicator, summary, metadata): """ - Converts a response from the TruSTAR /1.3/indicators/summaries endpoint - a MISP trustar_report object and adds the summary data and links as attributes. + Pulls enrichment data from the TruSTAR /indicators/summaries and /indicators/metadata endpoints + and creates a MISP trustar_report. - :param summaries: A TruSTAR Python SDK Page.generator object for generating - indicator summaries pages. + :param indicator: Value of the attribute + :summary: Indicator summary response object. + :metadata: Indicator response object. """ - for summary in summaries: - if summary.indicator_type in self.ENTITY_TYPE_MAPPINGS: - indicator_type = summary.indicator_type - indicator_value = summary.indicator_value - try: - enrichment_report = self.generate_enrichment_report(summary.to_dict(), metadata.to_dict()) - trustar_obj = MISPObject('trustar_report') - trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], - value=indicator_value) - trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", - value=json.dumps(enrichment_report, indent=4)) - report_link = self.generate_trustar_link(indicator_type, indicator_value) - trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=report_link) - self.misp_event.add_object(**trustar_obj) - except Exception as e: - misperrors['error'] = f"Error enriching data with TruSTAR -- {e}" + # Verify that the indicator type is supported by TruSTAR + if summary and summary.indicator_type in self.ENTITY_TYPE_MAPPINGS: + indicator_type = summary.indicator_type + elif metadata and metadata.type in self.ENTITY_TYPE_MAPPINGS: + indicator_type = metadata.type + else: + misperrors['error'] += " -- Attribute not found or not supported" + raise Exception + + try: + # Extract most relevant fields from indicator summary and metadata responses + enrichment_report = self.generate_enrichment_report(summary, metadata) + tags = self.extract_tags(enrichment_report) + + if enrichment_report: + trustar_obj = MISPObject('trustar_report') + trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], + value=indicator) + trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", + value=json.dumps(enrichment_report, indent=4)) + report_link = self.generate_trustar_link(indicator_type, indicator) + trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=report_link) + self.misp_event.add_object(**trustar_obj) + elif not tags: + raise Exception("No relevant data found") + + if tags: + for tag in tags: + self.misp_event.add_attribute_tag(tag, indicator) + except Exception as e: + misperrors['error'] += f" -- Error enriching attribute {indicator} -- {e}" + raise e def handler(q=False): @@ -139,14 +179,23 @@ def handler(q=False): trustar_parser = TruSTARParser(attribute, config) try: - metadata = trustar_parser.ts_client.get_indicators_metadata([attribute['value']]) - summaries = list( - trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE)) + metadata = trustar_parser.ts_client.get_indicators_metadata([Indicator(value=attribute['value'])])[0] except Exception as e: - misperrors['error'] = f"Unable to retrieve TruSTAR summary data: {e}" + metadata = None + misperrors['error'] += f" -- Could not retrieve indicator metadata from TruSTAR {e}" + + try: + summary = list( + trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE))[0] + except Exception as e: + summary = None + misperrors['error'] += f" -- Unable to retrieve TruSTAR summary data: {e}" + + try: + trustar_parser.parse_indicator_summary(attribute['value'], summary, metadata) + except Exception: return misperrors - trustar_parser.parse_indicator_summary(summaries) return trustar_parser.get_results() From 0b576faa68c1067af8a520085c37798bba146361 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sun, 9 Aug 2020 20:36:47 -0700 Subject: [PATCH 174/220] added comments --- misp_modules/modules/expansion/trustar_enrich.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 81238ad..6e615db 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -99,6 +99,7 @@ class TruSTARParser: :param metadata: Indicator metadata report. :return: Enrichment report. """ + # Preserve order of fields as they exist in SUMMARY_FIELDS and METADATA_FIELDS enrichment_report = OrderedDict() if summary: @@ -147,11 +148,13 @@ class TruSTARParser: trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=report_link) self.misp_event.add_object(**trustar_obj) elif not tags: + # If enrichment report is empty and there are no tags, nothing to add to attribute raise Exception("No relevant data found") if tags: for tag in tags: self.misp_event.add_attribute_tag(tag, indicator) + except Exception as e: misperrors['error'] += f" -- Error enriching attribute {indicator} -- {e}" raise e @@ -177,18 +180,18 @@ def handler(q=False): attribute = request['attribute'] trustar_parser = TruSTARParser(attribute, config) + metadata = None + summary = None try: metadata = trustar_parser.ts_client.get_indicators_metadata([Indicator(value=attribute['value'])])[0] except Exception as e: - metadata = None misperrors['error'] += f" -- Could not retrieve indicator metadata from TruSTAR {e}" try: summary = list( trustar_parser.ts_client.get_indicator_summaries([attribute['value']], page_size=MAX_PAGE_SIZE))[0] except Exception as e: - summary = None misperrors['error'] += f" -- Unable to retrieve TruSTAR summary data: {e}" try: From 91417d390b85f141b6ae337f7050833b1d846208 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Sun, 9 Aug 2020 20:41:52 -0700 Subject: [PATCH 175/220] added comments --- misp_modules/modules/expansion/trustar_enrich.py | 1 + 1 file changed, 1 insertion(+) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 6e615db..7b6ff3c 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -85,6 +85,7 @@ class TruSTARParser: redundancy. :param: Enrichment data. + :return: List of tags. """ if enrichment_report and enrichment_report.get('tags'): return [tag.get('name') for tag in enrichment_report.pop('tags')] From a3c01fa318b8b1a008e302885ff9161731349589 Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Mon, 10 Aug 2020 07:53:24 -0700 Subject: [PATCH 176/220] added comments --- misp_modules/modules/expansion/trustar_enrich.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index 7b6ff3c..33dd814 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -36,6 +36,7 @@ class TruSTARParser: 'SHA256': "sha256" } + # Relevant fields from each TruSTAR endpoint SUMMARY_FIELDS = ["severityLevel", "source", "score", "attributes"] METADATA_FIELDS = ["sightings", "first_seen", "last_seen", "tags"] @@ -140,13 +141,16 @@ class TruSTARParser: tags = self.extract_tags(enrichment_report) if enrichment_report: + # Create MISP trustar_report object and populate it with enrichment data trustar_obj = MISPObject('trustar_report') trustar_obj.add_attribute(indicator_type, attribute_type=self.ENTITY_TYPE_MAPPINGS[indicator_type], value=indicator) trustar_obj.add_attribute("INDICATOR_SUMMARY", attribute_type="text", value=json.dumps(enrichment_report, indent=4)) + report_link = self.generate_trustar_link(indicator_type, indicator) trustar_obj.add_attribute("REPORT_LINK", attribute_type="link", value=report_link) + self.misp_event.add_object(**trustar_obj) elif not tags: # If enrichment report is empty and there are no tags, nothing to add to attribute From bd7f7fa1f3340921a7c451d25e640950f3de9a9d Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 17 Aug 2020 17:34:21 +0200 Subject: [PATCH 177/220] fix: [virustotal] Resolve key error when user enrich hostname --- misp_modules/modules/expansion/virustotal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index b09de81..1e2c6c5 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -143,7 +143,7 @@ class VirusTotalParser(object): def parse_resolutions(self, resolutions, subdomains=None, uuids=None): domain_ip_object = MISPObject('domain-ip') - if self.attribute.type == 'domain': + if self.attribute.type in ('domain', 'hostname'): domain_ip_object.add_attribute('domain', type='domain', value=self.attribute.value) attribute_type, relation, key = ('ip-dst', 'ip', 'ip_address') else: From b5d7c9c7a3f7b56378569747fb84dd3f91e8e6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Andr=C3=A9?= Date: Mon, 24 Aug 2020 10:11:08 +0200 Subject: [PATCH 178/220] Disable correlation for detection-ratio in virustotal.py --- misp_modules/modules/expansion/virustotal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/virustotal.py b/misp_modules/modules/expansion/virustotal.py index 12f7552..6d3b52d 100644 --- a/misp_modules/modules/expansion/virustotal.py +++ b/misp_modules/modules/expansion/virustotal.py @@ -175,7 +175,7 @@ class VirusTotalParser(object): vt_object = MISPObject('virustotal-report') vt_object.add_attribute('permalink', type='link', value=query_result['permalink']) detection_ratio = '{}/{}'.format(query_result['positives'], query_result['total']) - vt_object.add_attribute('detection-ratio', type='text', value=detection_ratio) + vt_object.add_attribute('detection-ratio', type='text', value=detection_ratio, disable_correlation=True) self.misp_event.add_object(**vt_object) return vt_object.uuid From 8087c9a6a1eb10eba9f4da112f24f83b21b5d8a0 Mon Sep 17 00:00:00 2001 From: johannesh Date: Mon, 24 Aug 2020 11:19:15 +0200 Subject: [PATCH 179/220] Add proxy support and User-Agent header --- .../modules/expansion/recordedfuture.py | 261 +++++++++++++----- 1 file changed, 186 insertions(+), 75 deletions(-) diff --git a/misp_modules/modules/expansion/recordedfuture.py b/misp_modules/modules/expansion/recordedfuture.py index 8fdff09..537e997 100644 --- a/misp_modules/modules/expansion/recordedfuture.py +++ b/misp_modules/modules/expansion/recordedfuture.py @@ -1,36 +1,89 @@ import json import logging import requests +from requests.exceptions import HTTPError, ProxyError,\ + InvalidURL, ConnectTimeout, ConnectionError from . import check_input_attribute, checking_error, standard_error_message -from urllib.parse import quote +import platform +import os +from urllib.parse import quote, urlparse from pymisp import MISPAttribute, MISPEvent, MISPTag, MISPObject -moduleinfo = {'version': '1.0', 'author': 'Recorded Future', - 'description': 'Module to retrieve data from Recorded Future', - 'module-type': ['expansion', 'hover']} +moduleinfo = { + 'version': '1.0.1', + 'author': 'Recorded Future', + 'description': 'Module to retrieve data from Recorded Future', + 'module-type': ['expansion', 'hover'] +} -moduleconfig = ['token'] +moduleconfig = ['token', 'proxy_host', 'proxy_port', 'proxy_username', 'proxy_password'] misperrors = {'error': 'Error'} -mispattributes = {'input': ['ip', 'ip-src', 'ip-dst', 'domain', 'hostname', 'md5', 'sha1', 'sha256', - 'uri', 'url', 'vulnerability', 'weakness'], - 'output': ['ip', 'ip-src', 'ip-dst', 'domain', 'hostname', 'md5', 'sha1', 'sha256', - 'uri', 'url', 'vulnerability', 'weakness', 'email-src', 'text'], - 'format': 'misp_standard'} +ATTRIBUTES = [ + 'ip', + 'ip-src', + 'ip-dst', + 'domain', + 'hostname', + 'md5', + 'sha1', + 'sha256', + 'uri', + 'url', + 'vulnerability', + 'weakness' +] + +mispattributes = { + 'input': ATTRIBUTES, + 'output': ATTRIBUTES + ['email-src', 'text'], + 'format': 'misp_standard' +} LOGGER = logging.getLogger('recorded_future') LOGGER.setLevel(logging.INFO) -def rf_lookup(api_token: str, category: str, ioc: str) -> requests.Response: - """Do a lookup call using Recorded Future's ConnectAPI.""" - auth_header = {"X-RFToken": api_token} - parsed_ioc = quote(ioc, safe='') - url = f'https://api.recordedfuture.com/v2/{category}/{parsed_ioc}?fields=risk%2CrelatedEntities' - response = requests.get(url, headers=auth_header) - response.raise_for_status() - return response +class RequestHandler: + """A class for handling any outbound requests from this module.""" + def __init__(self): + self.session = requests.Session() + self.app_id = f'{os.path.basename(__file__)}/{moduleinfo["version"]} ({platform.platform()}) ' \ + f'misp_enrichment/{moduleinfo["version"]} python-requests/{requests.__version__}' + self.proxies = None + self.rf_token = None + + def get(self, url: str, headers: dict = None) -> requests.Response: + """General get method with proxy error handling.""" + try: + timeout = 7 if self.proxies else None + response = self.session.get(url, headers=headers, proxies=self.proxies, timeout=timeout) + response.raise_for_status() + return response + except (ConnectTimeout, ProxyError, InvalidURL) as error: + msg = f'Error connecting with proxy, please check the Recorded Future app proxy settings.' + LOGGER.error(f'{msg} Error: {error}') + misperrors['error'] = msg + raise + + def rf_lookup(self, category: str, ioc: str) -> requests.Response: + """Do a lookup call using Recorded Future's ConnectAPI.""" + parsed_ioc = quote(ioc, safe='') + url = f'https://api.recordedfuture.com/v2/{category}/{parsed_ioc}?fields=risk%2CrelatedEntities' + headers = {'X-RFToken': self.rf_token, + 'User-Agent': self.app_id} + try: + response = self.get(url, headers) + except HTTPError as error: + msg = f'Error when requesting data from Recorded Future. {error.response}: {error.response.reason}' + LOGGER.error(msg) + misperrors['error'] = msg + raise + return response + + +GLOBAL_REQUEST_HANDLER = RequestHandler() class GalaxyFinder: @@ -38,46 +91,45 @@ class GalaxyFinder: def __init__(self): self.session = requests.Session() self.sources = { - 'RelatedThreatActor': ['https://raw.githubusercontent.com/MISP/misp-galaxy/' - 'main/clusters/threat-actor.json'], - 'RelatedMalware': ['https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/banker.json', - 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/botnet.json', - 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/exploit-kit.json', - 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/rat.json', - 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/ransomware.json', - 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/malpedia.json'] + 'RelatedThreatActor': [ + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/threat-actor.json' + ], + 'RelatedMalware': [ + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/banker.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/botnet.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/exploit-kit.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/rat.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/ransomware.json', + 'https://raw.githubusercontent.com/MISP/misp-galaxy/main/clusters/malpedia.json' + ] } self.galaxy_clusters = {} - def pull_galaxy_cluster(self, related_type: str): + def pull_galaxy_cluster(self, related_type: str) -> None: """Fetches galaxy clusters for the related_type from the remote json files specified as self.sources.""" # Only fetch clusters if not fetched previously if not self.galaxy_clusters.get(related_type): for source in self.sources.get(related_type): - response = self.session.get(source) - if response.ok: + try: + response = GLOBAL_REQUEST_HANDLER.get(source) name = source.split('/')[-1].split('.')[0] self.galaxy_clusters[related_type] = {name: response.json()} - else: - LOGGER.info(f'pull_galaxy_cluster failed for source: {source},' - f' got response: {response}, {response.reason}.') + except ConnectionError as error: + LOGGER.warning(f'pull_galaxy_cluster failed for source: {source}, with error: {error}.') def find_galaxy_match(self, indicator: str, related_type: str) -> str: """Searches the clusters of the related_type for a match with the indicator. :returns the first matching galaxy string or an empty string if no galaxy match is found. """ self.pull_galaxy_cluster(related_type) - try: - for cluster_name, cluster in self.galaxy_clusters[related_type].items(): - for value in cluster['values']: - try: - if indicator in value['meta']['synonyms'] or indicator in value['value']: - value = value['value'] - return f'misp-galaxy:{cluster_name}="{value}"' - except KeyError: - pass - except KeyError: - pass + for cluster_name, cluster in self.galaxy_clusters.get(related_type, {}).items(): + for value in cluster['values']: + try: + if indicator in value['meta']['synonyms'] or indicator in value['value']: + value = value['value'] + return f'misp-galaxy:{cluster_name}="{value}"' + except KeyError: + pass return '' @@ -113,57 +165,70 @@ class RFEnricher: """Class for enriching an attribute with data from Recorded Future. The enrichment data is returned as a custom MISP object. """ - def __init__(self, api_token: str, attribute_props: dict): - self.api_token = api_token + def __init__(self, attribute_props: dict): self.event = MISPEvent() self.enrichment_object = MISPObject('Recorded Future Enrichment') - self.enrichment_object.from_dict(**{'meta-category': 'misc', - 'description': 'An object containing the enriched attribute and related ' - 'entities from Recorded Future.', - 'distribution': 0}) + description = ( + 'An object containing the enriched attribute and ' + 'related entities from Recorded Future.' + ) + self.enrichment_object.from_dict(**{ + 'meta-category': 'misc', + 'description': description, + 'distribution': 0 + }) # Create a copy of enriched attribute to add tags to temp_attr = MISPAttribute() temp_attr.from_dict(**attribute_props) self.enriched_attribute = MISPAttribute() - self.enriched_attribute.from_dict(**{'value': temp_attr.value, 'type': temp_attr.type, 'distribution': 0}) + self.enriched_attribute.from_dict(**{ + 'value': temp_attr.value, + 'type': temp_attr.type, + 'distribution': 0 + }) self.related_attributes = [] self.color_picker = RFColors() self.galaxy_finder = GalaxyFinder() # Mapping from MISP-type to RF-type - self.type_to_rf_category = {'ip': 'ip', 'ip-src': 'ip', 'ip-dst': 'ip', - 'domain': 'domain', 'hostname': 'domain', - 'md5': 'hash', 'sha1': 'hash', 'sha256': 'hash', - 'uri': 'url', 'url': 'url', - 'vulnerability': 'vulnerability', 'weakness': 'vulnerability'} + self.type_to_rf_category = { + 'ip': 'ip', + 'ip-src': 'ip', + 'ip-dst': 'ip', + 'domain': 'domain', + 'hostname': 'domain', + 'md5': 'hash', + 'sha1': 'hash', + 'sha256': 'hash', + 'uri': 'url', + 'url': 'url', + 'vulnerability': 'vulnerability', + 'weakness': 'vulnerability' + } # Related entities from RF portrayed as related attributes in MISP - self.related_attribute_types = ['RelatedIpAddress', 'RelatedInternetDomainName', 'RelatedHash', - 'RelatedEmailAddress', 'RelatedCyberVulnerability'] + self.related_attribute_types = [ + 'RelatedIpAddress', 'RelatedInternetDomainName', 'RelatedHash', + 'RelatedEmailAddress', 'RelatedCyberVulnerability' + ] # Related entities from RF portrayed as tags in MISP self.galaxy_tag_types = ['RelatedMalware', 'RelatedThreatActor'] - def enrich(self): + def enrich(self) -> None: """Run the enrichment.""" category = self.type_to_rf_category.get(self.enriched_attribute.type) - - try: - response = rf_lookup(self.api_token, category, self.enriched_attribute.value) - json_response = json.loads(response.content) - except requests.HTTPError as error: - misperrors['error'] = f'Error when requesting data from Recorded Future. ' \ - f'{error.response} : {error.response.reason}' - raise error + json_response = GLOBAL_REQUEST_HANDLER.rf_lookup(category, self.enriched_attribute.value) + response = json.loads(json_response.content) try: # Add risk score and risk rules as tags to the enriched attribute - risk_score = json_response['data']['risk']['score'] + risk_score = response['data']['risk']['score'] hex_color = self.color_picker.riskscore_color(risk_score) tag_name = f'recorded-future:risk-score="{risk_score}"' self.add_tag(tag_name, hex_color) - for evidence in json_response['data']['risk']['evidenceDetails']: + for evidence in response['data']['risk']['evidenceDetails']: risk_rule = evidence['rule'] criticality = evidence['criticality'] hex_color = self.color_picker.riskrule_color(criticality) @@ -171,7 +236,7 @@ class RFEnricher: self.add_tag(tag_name, hex_color) # Retrieve related entities - for related_entity in json_response['data']['relatedEntities']: + for related_entity in response['data']['relatedEntities']: related_type = related_entity['type'] if related_type in self.related_attribute_types: # Related entities returned as additional attributes @@ -191,9 +256,9 @@ class RFEnricher: galaxy_tags.append(galaxy) for galaxy in galaxy_tags: self.add_tag(galaxy) - except KeyError as error: + except KeyError: misperrors['error'] = 'Unexpected format in Recorded Future api response.' - raise error + raise def add_related_attribute(self, indicator: str, related_type: str) -> None: """Helper method for adding an indicator to the related attribute list.""" @@ -247,14 +312,54 @@ class RFEnricher: return {'results': result} +def get_proxy_settings(config: dict) -> dict: + """Returns proxy settings in the requests format. + If no proxy settings are set, return None.""" + proxies = None + host = config.get('proxy_host') + port = config.get('proxy_port') + username = config.get('proxy_username') + password = config.get('proxy_password') + + if host: + if not port: + misperrors['error'] = 'The recordedfuture_proxy_host config is set, ' \ + 'please also set the recordedfuture_proxy_port.' + raise KeyError + parsed = urlparse(host) + if 'http' in parsed.scheme: + scheme = 'http' + else: + scheme = parsed.scheme + netloc = parsed.netloc + host = f'{netloc}:{port}' + + if username: + if not password: + misperrors['error'] = 'The recordedfuture_proxy_username config is set, ' \ + 'please also set the recordedfuture_proxy_password.' + raise KeyError + auth = f'{username}:{password}' + host = auth + '@' + host + + proxies = { + 'http': f'{scheme}://{host}', + 'https': f'{scheme}://{host}' + } + + LOGGER.info(f'Proxy settings: {proxies}') + return proxies + + def handler(q=False): """Handle enrichment.""" if q is False: return False request = json.loads(q) - if request.get('config') and request['config'].get('token'): - token = request['config'].get('token') + config = request.get('config') + if config and config.get('token'): + GLOBAL_REQUEST_HANDLER.rf_token = config.get('token') else: misperrors['error'] = 'Missing Recorded Future token.' return misperrors @@ -263,11 +368,17 @@ def handler(q=False): if request['attribute']['type'] not in mispattributes['input']: return {'error': 'Unsupported attribute type.'} + try: + GLOBAL_REQUEST_HANDLER.proxies = get_proxy_settings(config) + except KeyError: + return misperrors + input_attribute = request.get('attribute') - rf_enricher = RFEnricher(token, input_attribute) + rf_enricher = RFEnricher(input_attribute) + try: rf_enricher.enrich() - except (requests.HTTPError, KeyError): + except (HTTPError, ConnectTimeout, ProxyError, InvalidURL, KeyError): return misperrors return rf_enricher.get_results() From 1349ef61a515b00d84b69bae06b6afd9d830e9b6 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 28 Aug 2020 16:55:50 +0200 Subject: [PATCH 180/220] chg: Turned the Shodan expansion module into a misp_standard format module - As expected with the misp_standard modules, the input is a full attribute and the module is able to return attributes and objects - There was a lot of data that was parsed as regkey attributes by the freetext import, the module now parses properly the different field of the result of the query returned by Shodan --- misp_modules/modules/expansion/shodan.py | 226 +++++++++++++++++++++-- 1 file changed, 206 insertions(+), 20 deletions(-) diff --git a/misp_modules/modules/expansion/shodan.py b/misp_modules/modules/expansion/shodan.py index 5a4b792..ecd5b82 100755 --- a/misp_modules/modules/expansion/shodan.py +++ b/misp_modules/modules/expansion/shodan.py @@ -5,38 +5,224 @@ try: import shodan except ImportError: print("shodan module not installed.") +from . import check_input_attribute, standard_error_message +from datetime import datetime +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['ip-src', 'ip-dst'], 'output': ['freetext']} -moduleinfo = {'version': '0.1', 'author': 'Raphaël Vinot', +mispattributes = {'input': ['ip-src', 'ip-dst'], + 'format': 'misp_standard'} +moduleinfo = {'version': '0.2', 'author': 'Raphaël Vinot', 'description': 'Query on Shodan', 'module-type': ['expansion']} moduleconfig = ['apikey'] +class ShodanParser(): + def __init__(self, attribute): + self.misp_event = MISPEvent() + self.attribute = MISPAttribute() + self.attribute.from_dict(**attribute) + self.misp_event.add_attribute(**self.attribute) + self.ip_address_mapping = { + 'asn': {'type': 'AS', 'object_relation': 'asn'}, + 'city': {'type': 'text', 'object_relation': 'city'}, + 'country_code': {'type': 'text', 'object_relation': 'country-code'}, + 'country_name': {'type': 'text', 'object_relation': 'country'}, + 'isp': {'type': 'text', 'object_relation': 'ISP'}, + 'latitude': {'type': 'float', 'object_relation': 'latitude'}, + 'longitude': {'type': 'float', 'object_relation': 'longitude'}, + 'org': {'type': 'text', 'object_relation': 'organization'}, + 'postal_code': {'type': 'text', 'object_relation': 'zipcode'}, + 'region_code': {'type': 'text', 'object_relation': 'region-code'} + } + self.ip_port_mapping = { + 'domains': {'type': 'domain', 'object_relation': 'domain'}, + 'hostnames': {'type': 'hostname', 'object_relation': 'hostname'} + } + self.vulnerability_mapping = { + 'cvss': {'type': 'float', 'object_relation': 'cvss-score'}, + 'summary': {'type': 'text', 'object_relation': 'summary'} + } + self.x509_mapping = { + 'bits': {'type': 'text', 'object_relation': 'pubkey-info-size'}, + 'expires': {'type': 'datetime', 'object_relation': 'validity-not-after'}, + 'issued': {'type': 'datetime', 'object_relation': 'validity-not-before'}, + 'issuer': {'type': 'text', 'object_relation': 'issuer'}, + 'serial': {'type': 'text', 'object_relation': 'serial-number'}, + 'sig_alg': {'type': 'text', 'object_relation': 'signature_algorithm'}, + 'subject': {'type': 'text', 'object_relation': 'subject'}, + 'type': {'type': 'text', 'object_relation': 'pubkey-info-algorithm'}, + 'version': {'type': 'text', 'object_relation': 'version'} + } + + def query_shodan(self, apikey): + # Query Shodan and get the results in a json blob + api = shodan.Shodan(apikey) + query_results = api.host(self.attribute.value) + + # Parse the information about the IP address used as input + ip_address_attributes = [] + for feature, mapping in self.ip_address_mapping.items(): + if query_results.get(feature): + attribute = {'value': query_results[feature]} + attribute.update(mapping) + ip_address_attributes.append(attribute) + if ip_address_attributes: + ip_address_object = MISPObject('ip-api-address') + for attribute in ip_address_attributes: + ip_address_object.add_attribute(**attribute) + ip_address_object.add_attribute(**self._get_source_attribute()) + ip_address_object.add_reference(self.attribute.uuid, 'describes') + self.misp_event.add_object(ip_address_object) + + # Parse the hostnames / domains and ports associated with the IP address + if query_results.get('ports'): + ip_port_object = MISPObject('ip-port') + ip_port_object.add_attribute(**self._get_source_attribute()) + feature = self.attribute.type.split('-')[1] + for port in query_results['ports']: + attribute = { + 'type': 'port', + 'object_relation': f'{feature}-port', + 'value': port + } + ip_port_object.add_attribute(**attribute) + for feature, mapping in self.ip_port_mapping.items(): + for value in query_results.get(feature, []): + attribute = {'value': value} + attribute.update(mapping) + ip_port_object.add_attribute(**attribute) + ip_port_object.add_reference(self.attribute.uuid, 'extends') + self.misp_event.add_object(ip_port_object) + else: + if any(query_results.get(feature) for feature in ('domains', 'hostnames')): + domain_ip_object = MISPObject('domain-ip') + domain_ip_object.add_attribute(**self._get_source_attribute()) + for feature in ('domains', 'hostnames'): + for value in query_results[feature]: + attribute = { + 'type': 'domain', + 'object_relation': 'domain', + 'value': value + } + domain_ip_object.add_attribute(**attribute) + domain_ip_object.add_reference(self.attribute.uuid, 'extends') + self.misp_event.add_object(domain_ip_object) + + # Parse data within the "data" field + if query_results.get('vulns'): + vulnerabilities = {} + for data in query_results['data']: + # Parse vulnerabilities + if data.get('vulns'): + for cve, vulnerability in data['vulns'].items(): + if cve not in vulnerabilities: + vulnerabilities[cve] = vulnerability + # Also parse the certificates + if data.get('ssl'): + self._parse_cert(data['ssl']) + for cve, vulnerability in vulnerabilities.items(): + vulnerability_object = MISPObject('vulnerability') + vulnerability_object.add_attribute(**{ + 'type': 'vulnerability', + 'object_relation': 'id', + 'value': cve + }) + for feature, mapping in self.vulnerability_mapping.items(): + if vulnerability.get(feature): + attribute = {'value': vulnerability[feature]} + attribute.update(mapping) + vulnerability_object.add_attribute(**attribute) + if vulnerability.get('references'): + for reference in vulnerability['references']: + vulnerability_object.add_attribute(**{ + 'type': 'link', + 'object_relation': 'references', + 'value': reference + }) + vulnerability_object.add_reference(self.attribute.uuid, 'vulnerability-of') + self.misp_event.add_object(vulnerability_object) + for cve_id in query_results['vulns']: + if cve_id not in vulnerabilities: + attribute = { + 'type': 'vulnerability', + 'value': cve_id + } + self.misp_event.add_attribute(**attribute) + else: + # We have no vulnerability data, we only check if we have + # certificates within the "data" field + for data in query_results['data']: + if data.get('ssl'): + self._parse_cert(data['ssl']['cert']) + + def get_result(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + + # When we want to add the IP address information in objects such as the + # domain-ip or ip-port objects referencing the input IP address attribute + def _get_source_attribute(self): + return { + 'type': self.attribute.type, + 'object_relation': self.attribute.type, + 'value': self.attribute.value + } + + def _parse_cert(self, certificate): + x509_object = MISPObject('x509') + for feature in ('serial', 'sig_alg', 'version'): + if certificate.get(feature): + attribute = {'value': certificate[feature]} + attribute.update(self.x509_mapping[feature]) + x509_object.add_attribute(**attribute) + # Parse issuer and subject value + for feature in ('issuer', 'subject'): + if certificate.get(feature): + attribute_value = (f'{identifier}={value}' for identifier, value in certificate[feature].items()) + attribute = {'value': f'/{"/".join(attribute_value)}'} + attribute.update(self.x509_mapping[feature]) + x509_object.add_attribute(**attribute) + # Parse datetime attributes + for feature in ('expires', 'issued'): + if certificate.get(feature): + attribute = {'value': datetime.strptime(certificate[feature], '%Y%m%d%H%M%SZ')} + attribute.update(self.x509_mapping[feature]) + x509_object.add_attribute(**attribute) + # Parse fingerprints + if certificate.get('fingerprint'): + for hash_type, hash_value in certificate['fingerprint'].items(): + x509_object.add_attribute(**{ + 'type': f'x509-fingerprint-{hash_type}', + 'object_relation': f'x509-fingerprint-{hash_type}', + 'value': hash_value + }) + # Parse public key related info + if certificate.get('pubkey'): + for feature, value in certificate['pubkey'].items(): + attribute = {'value': value} + attribute.update(self.x509_mapping[feature]) + x509_object.add_attribute(**attribute) + x509_object.add_reference(self.attribute.uuid, 'identifies') + self.misp_event.add_object(x509_object) + def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('ip-src'): - toquery = request['ip-src'] - elif request.get('ip-dst'): - toquery = request['ip-dst'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors - - if not request.get('config') or not request['config'].get('apikey'): - misperrors['error'] = 'Shodan authentication is missing' - return misperrors - api = shodan.Shodan(request['config'].get('apikey')) - - return handle_expansion(api, toquery) - - -def handle_expansion(api, domain): - return {'results': [{'types': mispattributes['output'], 'values': json.dumps(api.host(domain))}]} + if not request.get('config', {}).get('apikey'): + return {'error': 'Shodan authentication is missing'} + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} + shodan_parser = ShodanParser(attribute) + shodan_parser.query_shodan(request['config']['apikey']) + return shodan_parser.get_result() def introspection(): From ae1016946bbbe0dd2652e0c383b8c5214fca4527 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 28 Aug 2020 17:30:23 +0200 Subject: [PATCH 181/220] fix: Making pep8 happy --- misp_modules/modules/expansion/recordedfuture.py | 2 +- misp_modules/modules/expansion/shodan.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/recordedfuture.py b/misp_modules/modules/expansion/recordedfuture.py index 537e997..ccea31b 100644 --- a/misp_modules/modules/expansion/recordedfuture.py +++ b/misp_modules/modules/expansion/recordedfuture.py @@ -62,7 +62,7 @@ class RequestHandler: response.raise_for_status() return response except (ConnectTimeout, ProxyError, InvalidURL) as error: - msg = f'Error connecting with proxy, please check the Recorded Future app proxy settings.' + msg = 'Error connecting with proxy, please check the Recorded Future app proxy settings.' LOGGER.error(f'{msg} Error: {error}') misperrors['error'] = msg raise diff --git a/misp_modules/modules/expansion/shodan.py b/misp_modules/modules/expansion/shodan.py index ecd5b82..f295deb 100755 --- a/misp_modules/modules/expansion/shodan.py +++ b/misp_modules/modules/expansion/shodan.py @@ -209,6 +209,7 @@ class ShodanParser(): x509_object.add_reference(self.attribute.uuid, 'identifies') self.misp_event.add_object(x509_object) + def handler(q=False): if q is False: return False From 3101e5bc26ee3e45afed16dfb7a5b45ac70a270c Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 8 Sep 2020 16:08:57 +0200 Subject: [PATCH 182/220] chg: Updated the bgpranking expansion module to return MISP objects - The module no longer returns freetext, since the result returned to the freetext import as text only allowed MISP to parse the same AS number as the input attribute. - The new result returned with the updated module is an asn object describing more precisely the AS number, and its ranking for a given day --- misp_modules/modules/expansion/bgpranking.py | 72 ++++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/misp_modules/modules/expansion/bgpranking.py b/misp_modules/modules/expansion/bgpranking.py index b01088d..c021d62 100755 --- a/misp_modules/modules/expansion/bgpranking.py +++ b/misp_modules/modules/expansion/bgpranking.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- import json -from datetime import date, timedelta +from . import check_input_attribute, standard_error_message +from datetime import date, datetime, timedelta from pybgpranking import BGPRanking +from pymisp import MISPAttribute, MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['AS'], 'output': ['freetext']} +mispattributes = {'input': ['AS'], 'format': 'misp_standard'} moduleinfo = {'version': '0.1', 'author': 'Raphaël Vinot', - 'description': 'Query an ASN Description history service (https://github.com/CIRCL/ASN-Description-History.git)', + 'description': 'Query BGP Ranking to get the ranking of an Autonomous System number.', 'module-type': ['expansion', 'hover']} @@ -15,19 +17,65 @@ def handler(q=False): if q is False: return False request = json.loads(q) - if request.get('AS'): - toquery = request['AS'] - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + toquery = request['attribute'] + if toquery['type'] not in mispattributes['input']: + return {'error': 'Unsupported attribute type.'} bgpranking = BGPRanking() - values = bgpranking.query(toquery, date=(date.today() - timedelta(1)).isoformat()) + value_toquery = int(toquery['value'][2:]) if toquery['value'].startswith('AS') else int(toquery['value']) + values = bgpranking.query(value_toquery, date=(date.today() - timedelta(1)).isoformat()) - if not values: - misperrors['error'] = 'Unable to find the ASN in BGP Ranking' + if not values['response'] or not values['response']['asn_description']: + misperrors['error'] = 'There is no result about this ASN in BGP Ranking' return misperrors - return {'results': [{'types': mispattributes['output'], 'values': values}]} + + event = MISPEvent() + attribute = MISPAttribute() + attribute.from_dict(**toquery) + event.add_attribute(**attribute) + + asn_object = MISPObject('asn') + asn_object.add_attribute(**{ + 'type': 'AS', + 'object_relation': 'asn', + 'value': values['meta']['asn'] + }) + description, country = values['response']['asn_description'].split(', ') + for relation, value in zip(('description', 'country'), (description, country)): + asn_object.add_attribute(**{ + 'type': 'text', + 'object_relation': relation, + 'value': value + }) + + mapping = { + 'address_family': {'type': 'text', 'object_relation': 'address-family'}, + 'date': {'type': 'datetime', 'object_relation': 'date'}, + 'position': {'type': 'float', 'object_relation': 'position'}, + 'rank': {'type': 'float', 'object_relation': 'ranking'} + } + bgp_object = MISPObject('bgp-ranking') + for feature in ('rank', 'position'): + bgp_attribute = {'value': values['response']['ranking'][feature]} + bgp_attribute.update(mapping[feature]) + bgp_object.add_attribute(**bgp_attribute) + date_attribute = {'value': datetime.strptime(values['meta']['date'], '%Y-%m-%d')} + date_attribute.update(mapping['date']) + bgp_object.add_attribute(**date_attribute) + address_attribute = {'value': values['meta']['address_family']} + address_attribute.update(mapping['address_family']) + bgp_object.add_attribute(**address_attribute) + + asn_object.add_reference(attribute.uuid, 'describes') + asn_object.add_reference(bgp_object.uuid, 'ranked-with') + event.add_object(asn_object) + event.add_object(bgp_object) + + event = json.loads(event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} def introspection(): From 589a0a03210b1f1734ff507919fd01c7f154bbf8 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 8 Sep 2020 16:15:23 +0200 Subject: [PATCH 183/220] chg: Updated documentation for the recently updated bgpranking module --- README.md | 2 +- doc/README.md | 4 ++-- doc/expansion/bgpranking.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 83b4dc6..26dce03 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [AssemblyLine submit](misp_modules/modules/expansion/assemblyline_submit.py) - an expansion module to submit samples and urls to AssemblyLine. * [AssemblyLine query](misp_modules/modules/expansion/assemblyline_query.py) - an expansion module to query AssemblyLine and parse the full submission report. * [Backscatter.io](misp_modules/modules/expansion/backscatter_io.py) - a hover and expansion module to expand an IP address with mass-scanning observations. -* [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description, its history, and position in BGP Ranking. +* [BGP Ranking](misp_modules/modules/expansion/bgpranking.py) - a hover and expansion module to expand an AS number with the ASN description and its ranking and position in BGP Ranking. * [RansomcoinDB check](misp_modules/modules/expansion/ransomcoindb.py) - An expansion hover module to query the [ransomcoinDB](https://ransomcoindb.concinnity-risks.com): it contains mapping between BTC addresses and malware hashes. Enrich MISP by querying for BTC -> hash or hash -> BTC addresses. * [BTC scam check](misp_modules/modules/expansion/btc_scam_check.py) - An expansion hover module to instantly check if a BTC address has been abused. * [BTC transactions](misp_modules/modules/expansion/btc_steroids.py) - An expansion hover module to get a blockchain balance and the transactions from a BTC address in MISP. diff --git a/doc/README.md b/doc/README.md index 7979348..6469dd0 100644 --- a/doc/README.md +++ b/doc/README.md @@ -108,13 +108,13 @@ Query backscatter.io (https://backscatter.io/). Query BGP Ranking (https://bgpranking-ng.circl.lu/). - **features**: ->The module takes an AS number attribute as input and displays its description and history, and position in BGP Ranking. +>The module takes an AS number attribute as input and displays its description as well as its ranking position in BGP Ranking for a given day. > > - **input**: >Autonomous system number. - **output**: ->Text containing a description of the ASN, its history, and the position in BGP Ranking. +>An asn object with its related bgp-ranking object. - **references**: >https://github.com/D4-project/BGP-Ranking/ - **requirements**: diff --git a/doc/expansion/bgpranking.json b/doc/expansion/bgpranking.json index a98b780..4695aa1 100644 --- a/doc/expansion/bgpranking.json +++ b/doc/expansion/bgpranking.json @@ -1,8 +1,8 @@ { "description": "Query BGP Ranking (https://bgpranking-ng.circl.lu/).", "requirements": ["pybgpranking python library"], - "features": "The module takes an AS number attribute as input and displays its description and history, and position in BGP Ranking.\n\n", + "features": "The module takes an AS number attribute as input and displays its description as well as its ranking position in BGP Ranking for a given day.\n\n", "references": ["https://github.com/D4-project/BGP-Ranking/"], "input": "Autonomous system number.", - "output": "Text containing a description of the ASN, its history, and the position in BGP Ranking." + "output": "An asn object with its related bgp-ranking object." } From 9f315f1728d54796bb05db52839333643c313b2b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 8 Sep 2020 16:24:41 +0200 Subject: [PATCH 184/220] chg: Updated the bgpranking expansion module test --- tests/test_expansions.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index a56fbe7..1aa0f7a 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -97,9 +97,16 @@ class TestExpansions(unittest.TestCase): self.assertEqual(self.get_errors(response), 'An API key for APIVoid is required.') def test_bgpranking(self): - query = {"module": "bgpranking", "AS": "13335"} + query = { + "module": "bgpranking", + "attribute": { + "type": "AS", + "value": "13335", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d" + } + } response = self.misp_modules_post(query) - self.assertEqual(self.get_values(response)['response']['asn_description'], 'CLOUDFLARENET, US') + self.assertEqual(self.get_object(response), 'asn') def test_btc_steroids(self): query = {"module": "btc_steroids", "btc": "1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA"} From 2dde6e8757bc43cffde65e238ef370c461b71cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 9 Sep 2020 10:56:01 +0200 Subject: [PATCH 185/220] fix: Typo in EMailObject Fix #427 --- misp_modules/modules/import_mod/email_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/import_mod/email_import.py b/misp_modules/modules/import_mod/email_import.py index 114f8c9..7453dcd 100644 --- a/misp_modules/modules/import_mod/email_import.py +++ b/misp_modules/modules/import_mod/email_import.py @@ -42,7 +42,7 @@ def handler(q=False): # request data is always base 64 byte encoded data = base64.b64decode(request["data"]) - email_object = EMailObject(pseudofile=BytesIO(data), attach_original_mail=True, standalone=False) + email_object = EMailObject(pseudofile=BytesIO(data), attach_original_email=True, standalone=False) # Check if we were given a configuration config = request.get("config", {}) From c5abf8980534b127785d97c8a292534cf8d88a3b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 28 Sep 2020 12:34:00 +0200 Subject: [PATCH 186/220] fix: [virustotal_public] Resolve key error when user enrich hostname - Same as #424 --- misp_modules/modules/expansion/virustotal_public.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/virustotal_public.py b/misp_modules/modules/expansion/virustotal_public.py index 6ffb7f9..989e48d 100644 --- a/misp_modules/modules/expansion/virustotal_public.py +++ b/misp_modules/modules/expansion/virustotal_public.py @@ -37,7 +37,7 @@ class VirusTotalParser(): def parse_resolutions(self, resolutions, subdomains=None, uuids=None): domain_ip_object = MISPObject('domain-ip') - if self.attribute.type == 'domain': + if self.attribute.type in ('domain', 'hostname'): domain_ip_object.add_attribute('domain', type='domain', value=self.attribute.value) attribute_type, relation, key = ('ip-dst', 'ip', 'ip_address') else: From 14aa6e2d1a0a4933f1d26c181215487bf25c6cf4 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 1 Oct 2020 22:44:39 +0200 Subject: [PATCH 187/220] fix: [cve_advanced] Avoiding potential MISP object references issues - Adding objects as dictionaries in an event may cause issues in some cases. It is better to pass the MISP object as is, as it is already a valid object since the MISPObject class is used --- misp_modules/modules/expansion/cve_advanced.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misp_modules/modules/expansion/cve_advanced.py b/misp_modules/modules/expansion/cve_advanced.py index cd36655..d15711f 100644 --- a/misp_modules/modules/expansion/cve_advanced.py +++ b/misp_modules/modules/expansion/cve_advanced.py @@ -56,7 +56,7 @@ class VulnerabilityParser(): value = value['title'] vulnerability_object.add_attribute(relation, **{'type': attribute_type, 'value': value}) vulnerability_object.add_reference(self.attribute['uuid'], 'related-to') - self.misp_event.add_object(**vulnerability_object) + self.misp_event.add_object(vulnerability_object) if 'cwe' in self.vulnerability and self.vulnerability['cwe'] not in ('Unknown', 'NVD-CWE-noinfo'): self.__parse_weakness(vulnerability_object.uuid) if 'capec' in self.vulnerability: @@ -79,7 +79,7 @@ class VulnerabilityParser(): for related_weakness in capec['related_weakness']: attribute = dict(type='weakness', value="CWE-{}".format(related_weakness)) capec_object.add_attribute('related-weakness', **attribute) - self.misp_event.add_object(**capec_object) + self.misp_event.add_object(capec_object) self.references[vulnerability_uuid].append(dict(referenced_uuid=capec_object.uuid, relationship_type='targeted-by')) @@ -95,7 +95,7 @@ class VulnerabilityParser(): for feature, relation in self.weakness_mapping.items(): if cwe.get(feature): weakness_object.add_attribute(relation, **dict(type=attribute_type, value=cwe[feature])) - self.misp_event.add_object(**weakness_object) + self.misp_event.add_object(weakness_object) self.references[vulnerability_uuid].append(dict(referenced_uuid=weakness_object.uuid, relationship_type='weakened-by')) break From 9a766d601089ff17e7c0f99561fe2d1e697073f4 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 1 Oct 2020 23:25:39 +0200 Subject: [PATCH 188/220] add: New module using socialscan library to check email addresses and usernames linked to accounts on online platforms --- misp_modules/modules/expansion/__init__.py | 2 +- misp_modules/modules/expansion/socialscan.py | 102 +++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/expansion/socialscan.py diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 1b6d2bb..10254e4 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', - 'trustar_enrich', 'recordedfuture'] + 'trustar_enrich', 'recordedfuture', 'socialscan'] minimum_required_fields = ('type', 'uuid', 'value') diff --git a/misp_modules/modules/expansion/socialscan.py b/misp_modules/modules/expansion/socialscan.py new file mode 100644 index 0000000..7c59a35 --- /dev/null +++ b/misp_modules/modules/expansion/socialscan.py @@ -0,0 +1,102 @@ +import json +from socialscan.platforms import Platforms +from socialscan.util import sync_execute_queries + +moduleinfo = { + 'version': '1', + 'author': 'Christian Studer', + 'description': 'Module to query several online platforms to look for existing accounts.', + 'module-type': ['hover'] +} +mispattributes = { + 'input': [ + 'github-username', + 'target-user', + 'email', + 'email-src', + 'email-dst', + 'target-email', + 'whois-registrant-email' + ], + 'output': ['text'] +} +moduleconfig = [] + +_PLATFORMS = [ + Platforms.INSTAGRAM, + Platforms.TWITTER, + Platforms.GITHUB, + Platforms.TUMBLR, + Platforms.LASTFM +] +_EMAIL_PLATFORMS = [ + Platforms.PINTEREST, + Platforms.SPOTIFY, + Platforms.FIREFOX +] +_EMAIL_PLATFORMS.extend(_PLATFORMS) +_USERNAME_PLATFORMS = [ + Platforms.SNAPCHAT, + Platforms.GITLAB, + Platforms.REDDIT, + Platforms.YAHOO +] +_USERNAME_PLATFORMS.extend(_PLATFORMS) + + +def parse_results(query_results, feature): + results = [] + for result in query_results: + if not result.success: + results.append(f'Unable to retrieve the {feature} on {result.platform}.') + continue + if not result.valid: + results.append(f'Invalid response from {result.platform}.') + continue + statement = 'No account' if result.available else 'There is an account' + results.append(f'{statement} linked to the {feature} on {result.platform}.') + to_return = [ + { + 'types': mispattributes['output'], + 'values': result + } for result in results + ] + print(json.dumps(to_return, indent=4)) + return {'results': to_return} + + +def parse_email(email): + results = sync_execute_queries([email], platforms=_EMAIL_PLATFORMS) + return parse_results(results, 'email address') + + +def parse_username(username, platforms=_USERNAME_PLATFORMS): + results = sync_execute_queries([username], platforms=platforms) + return parse_results(results, 'username') + + +def parse_github_username(username): + return parse_username(username, platforms=[Platforms.GITHUB]) + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if request.get('github-username'): + return parse_github_username(request['github-username']) + if request.get('target-user'): + return parse_username(request['target-user']) + for attribute_type in mispattributes['input'][2:]: + if request.get(attribute_type): + return parse_email(request[attribute_type]) + return {'error': 'Unsupported attributes type'} + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 39904b7f6c0eb0a5d030bbfb623bb41e3631ba06 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 1 Oct 2020 23:27:43 +0200 Subject: [PATCH 189/220] chg: Added socialscan library in Pipfile and updated the lock file --- Pipfile | 1 + Pipfile.lock | 870 ++++++++++++++++++++++++++++----------------------- 2 files changed, 471 insertions(+), 400 deletions(-) diff --git a/Pipfile b/Pipfile index 1169368..5bd615c 100644 --- a/Pipfile +++ b/Pipfile @@ -61,6 +61,7 @@ apiosintDS = "*" assemblyline_client = "*" vt-graph-api = "*" trustar = "*" +socialscan="*" [requires] python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index 73aeaed..23af91a 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c2d937b384431e4b313b29bb02db0bd1d3a866ddcb7c6e91cbfa34f88d351b59" + "sha256": "41a685412700814c27cc8835a0a650a5f44dab172239cd06a7cb02f201a63e73" }, "pipfile-spec": 6, "requires": { @@ -57,11 +57,10 @@ }, "assemblyline-client": { "hashes": [ - "sha256:6f45cab3be3ec60984a5c2049d46dac80d4e3d4f3d9538220518a44c7a6ddb15", - "sha256:971371065f2b41027325bf9fa9c72960262a446c7e08bda57865d34dcc4108b0" + "sha256:39c54d9f49a8299de5bffc2422138f0254e2d9ddc49fec7a41b537194e4be29a" ], "index": "pypi", - "version": "==3.7.3" + "version": "==4.0.0" }, "async-timeout": { "hashes": [ @@ -73,11 +72,11 @@ }, "attrs": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", + "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==20.2.0" }, "backscatter": { "hashes": [ @@ -89,12 +88,12 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7", - "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8", - "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c" + "sha256:1edf5e39f3a5bc6e38b235b369128416c7239b34f692acccececb040233032a1", + "sha256:5dfe44f8fddc89ac5453f02659d3ab1668f2c0d9684839f0785037e8c6d9ac8d", + "sha256:645d833a828722357038299b7f6879940c11dddd95b900fe5387c258b72bb883" ], "index": "pypi", - "version": "==4.9.1" + "version": "==4.9.2" }, "blockchain": { "hashes": [ @@ -112,36 +111,44 @@ }, "cffi": { "hashes": [ - "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff", - "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b", - "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac", - "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0", - "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384", - "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26", - "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6", - "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b", - "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e", - "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd", - "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2", - "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66", - "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc", - "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8", - "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55", - "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4", - "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5", - "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d", - "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78", - "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa", - "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793", - "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f", - "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a", - "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f", - "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30", - "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f", - "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3", - "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c" + "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", + "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", + "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", + "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", + "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", + "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", + "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", + "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", + "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", + "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", + "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", + "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", + "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", + "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", + "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", + "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", + "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", + "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", + "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", + "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", + "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", + "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", + "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", + "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", + "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", + "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", + "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", + "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", + "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", + "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", + "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", + "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", + "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", + "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", + "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", + "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" ], - "version": "==1.14.0" + "version": "==1.14.3" }, "chardet": { "hashes": [ @@ -183,27 +190,30 @@ }, "cryptography": { "hashes": [ - "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", - "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b", - "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5", - "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf", - "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e", - "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b", - "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae", - "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b", - "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0", - "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b", - "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d", - "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229", - "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3", - "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365", - "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55", - "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270", - "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e", - "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785", - "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0" + "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499", + "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154", + "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6", + "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49", + "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f", + "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396", + "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719", + "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db", + "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70", + "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536", + "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe", + "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba", + "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d", + "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7", + "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490", + "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8", + "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921", + "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118", + "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba", + "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3", + "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc", + "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2" ], - "version": "==2.9.2" + "version": "==3.1.1" }, "decorator": { "hashes": [ @@ -222,11 +232,11 @@ }, "dnspython": { "hashes": [ - "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01", - "sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d" + "sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7", + "sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d" ], "index": "pypi", - "version": "==1.16.0" + "version": "==2.0.0" }, "domaintools-api": { "hashes": [ @@ -273,11 +283,11 @@ }, "geoip2": { "hashes": [ - "sha256:5869e987bc54c0d707264fec4710661332cc38d2dca5a7f9bb5362d0308e2ce0", - "sha256:99ec12d2f1271a73a0a4a2b663fe6ce25fd02289c0a6bef05c0a1c3b30ee95a4" + "sha256:57d8d15de2527e0697bbef44fc16812bba709f03a07ef99297bd56c1df3b1efd", + "sha256:707025542ef076bd8fd80e97138bebdb7812527b2a007d141a27ad98b0370fff" ], "index": "pypi", - "version": "==3.0.0" + "version": "==4.1.0" }, "httplib2": { "hashes": [ @@ -310,10 +320,16 @@ }, "jbxapi": { "hashes": [ - "sha256:58eb7d77a52169309e2322ce874c0f00a7900a515d1d0798ff85973cdb2766e3" + "sha256:a5dc57e418363f5e2ab39f79fdfdb71dbad758cfc2ff254a3eb8e353bc9994ae" ], "index": "pypi", - "version": "==3.8.0" + "version": "==3.10.0" + }, + "json-log-formatter": { + "hashes": [ + "sha256:ee187c9a80936cbf1259f73573973450fc24b84a4fb54e53eb0dcff86ea1e759" + ], + "version": "==0.3.0" }, "jsonschema": { "hashes": [ @@ -343,36 +359,40 @@ }, "lxml": { "hashes": [ - "sha256:06748c7192eab0f48e3d35a7adae609a329c6257495d5e53878003660dc0fec6", - "sha256:0790ddca3f825dd914978c94c2545dbea5f56f008b050e835403714babe62a5f", - "sha256:1aa7a6197c1cdd65d974f3e4953764eee3d9c7b67e3966616b41fab7f8f516b7", - "sha256:22c6d34fdb0e65d5f782a4d1a1edb52e0a8365858dafb1c08cb1d16546cf0786", - "sha256:2754d4406438c83144f9ffd3628bbe2dcc6d62b20dbc5c1ec4bc4385e5d44b42", - "sha256:27ee0faf8077c7c1a589573b1450743011117f1aa1a91d5ae776bbc5ca6070f2", - "sha256:2b02c106709466a93ed424454ce4c970791c486d5fcdf52b0d822a7e29789626", - "sha256:2d1ddce96cf15f1254a68dba6935e6e0f1fe39247de631c115e84dd404a6f031", - "sha256:4f282737d187ae723b2633856085c31ae5d4d432968b7f3f478a48a54835f5c4", - "sha256:51bb4edeb36d24ec97eb3e6a6007be128b720114f9a875d6b370317d62ac80b9", - "sha256:7eee37c1b9815e6505847aa5e68f192e8a1b730c5c7ead39ff317fde9ce29448", - "sha256:7fd88cb91a470b383aafad554c3fe1ccf6dfb2456ff0e84b95335d582a799804", - "sha256:9144ce36ca0824b29ebc2e02ca186e54040ebb224292072250467190fb613b96", - "sha256:925baf6ff1ef2c45169f548cc85204433e061360bfa7d01e1be7ae38bef73194", - "sha256:a636346c6c0e1092ffc202d97ec1843a75937d8c98aaf6771348ad6422e44bb0", - "sha256:a87dbee7ad9dce3aaefada2081843caf08a44a8f52e03e0a4cc5819f8398f2f4", - "sha256:a9e3b8011388e7e373565daa5e92f6c9cb844790dc18e43073212bb3e76f7007", - "sha256:afb53edf1046599991fb4a7d03e601ab5f5422a5435c47ee6ba91ec3b61416a6", - "sha256:b26719890c79a1dae7d53acac5f089d66fd8cc68a81f4e4bd355e45470dc25e1", - "sha256:b7462cdab6fffcda853338e1741ce99706cdf880d921b5a769202ea7b94e8528", - "sha256:b77975465234ff49fdad871c08aa747aae06f5e5be62866595057c43f8d2f62c", - "sha256:c47a8a5d00060122ca5908909478abce7bbf62d812e3fc35c6c802df8fb01fe7", - "sha256:c79e5debbe092e3c93ca4aee44c9a7631bdd407b2871cb541b979fd350bbbc29", - "sha256:d8d40e0121ca1606aa9e78c28a3a7d88a05c06b3ca61630242cded87d8ce55fa", - "sha256:ee2be8b8f72a2772e72ab926a3bccebf47bb727bda41ae070dc91d1fb759b726", - "sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9", - "sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529" + "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f", + "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730", + "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f", + "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1", + "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3", + "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7", + "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a", + "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe", + "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1", + "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e", + "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d", + "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20", + "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae", + "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5", + "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba", + "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293", + "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a", + "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6", + "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88", + "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed", + "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843", + "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443", + "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0", + "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304", + "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258", + "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6", + "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1", + "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481", + "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef", + "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd", + "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee" ], "index": "pypi", - "version": "==4.5.1" + "version": "==4.5.2" }, "maclookup": { "hashes": [ @@ -384,10 +404,10 @@ }, "maxminddb": { "hashes": [ - "sha256:f4d28823d9ca23323d113dc7af8db2087aa4f657fafc64ff8f7a8afda871425b" + "sha256:b95d8ed21799e6604683669c7ed3c6a184fcd92434d5762dccdb139b4f29e597" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.5.4" + "markers": "python_version >= '3.6'", + "version": "==2.0.2" }, "misp-modules": { "editable": true, @@ -425,35 +445,35 @@ }, "numpy": { "hashes": [ - "sha256:13af0184177469192d80db9bd02619f6fa8b922f9f327e077d6f2a6acb1ce1c0", - "sha256:26a45798ca2a4e168d00de75d4a524abf5907949231512f372b217ede3429e98", - "sha256:26f509450db547e4dfa3ec739419b31edad646d21fb8d0ed0734188b35ff6b27", - "sha256:30a59fb41bb6b8c465ab50d60a1b298d1cd7b85274e71f38af5a75d6c475d2d2", - "sha256:33c623ef9ca5e19e05991f127c1be5aeb1ab5cdf30cb1c5cf3960752e58b599b", - "sha256:356f96c9fbec59974a592452ab6a036cd6f180822a60b529a975c9467fcd5f23", - "sha256:3c40c827d36c6d1c3cf413694d7dc843d50997ebffbc7c87d888a203ed6403a7", - "sha256:4d054f013a1983551254e2379385e359884e5af105e3efe00418977d02f634a7", - "sha256:63d971bb211ad3ca37b2adecdd5365f40f3b741a455beecba70fd0dde8b2a4cb", - "sha256:658624a11f6e1c252b2cd170d94bf28c8f9410acab9f2fd4369e11e1cd4e1aaf", - "sha256:76766cc80d6128750075378d3bb7812cf146415bd29b588616f72c943c00d598", - "sha256:7b57f26e5e6ee2f14f960db46bd58ffdca25ca06dd997729b1b179fddd35f5a3", - "sha256:7b852817800eb02e109ae4a9cef2beda8dd50d98b76b6cfb7b5c0099d27b52d4", - "sha256:8cde829f14bd38f6da7b2954be0f2837043e8b8d7a9110ec5e318ae6bf706610", - "sha256:a2e3a39f43f0ce95204beb8fe0831199542ccab1e0c6e486a0b4947256215632", - "sha256:a86c962e211f37edd61d6e11bb4df7eddc4a519a38a856e20a6498c319efa6b0", - "sha256:a8705c5073fe3fcc297fb8e0b31aa794e05af6a329e81b7ca4ffecab7f2b95ef", - "sha256:b6aaeadf1e4866ca0fdf7bb4eed25e521ae21a7947c59f78154b24fc7abbe1dd", - "sha256:be62aeff8f2f054eff7725f502f6228298891fd648dc2630e03e44bf63e8cee0", - "sha256:c2edbb783c841e36ca0fa159f0ae97a88ce8137fb3a6cd82eae77349ba4b607b", - "sha256:cbe326f6d364375a8e5a8ccb7e9cd73f4b2f6dc3b2ed205633a0db8243e2a96a", - "sha256:d34fbb98ad0d6b563b95de852a284074514331e6b9da0a9fc894fb1cdae7a79e", - "sha256:d97a86937cf9970453c3b62abb55a6475f173347b4cde7f8dcdb48c8e1b9952d", - "sha256:dd53d7c4a69e766e4900f29db5872f5824a06827d594427cf1a4aa542818b796", - "sha256:df1889701e2dfd8ba4dc9b1a010f0a60950077fb5242bb92c8b5c7f1a6f2668a", - "sha256:fa1fe75b4a9e18b66ae7f0b122543c42debcf800aaafa0212aaff3ad273c2596" + "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5", + "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8", + "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf", + "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c", + "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65", + "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716", + "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a", + "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e", + "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b", + "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45", + "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d", + "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d", + "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02", + "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c", + "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526", + "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15", + "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d", + "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a", + "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1", + "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a", + "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1", + "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9", + "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4", + "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c", + "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd", + "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8" ], "markers": "python_version >= '3.6'", - "version": "==1.19.0" + "version": "==1.19.2" }, "oauth2": { "hashes": [ @@ -470,51 +490,47 @@ }, "opencv-python": { "hashes": [ - "sha256:068928b9907b3d3acd53b129062557d6b0b8b324bfade77f028dbe4dfe482bf2", - "sha256:0e7c91718351449877c2d4141abd64eee1f9c8701bcfaf4e8627bd023e303368", - "sha256:1ab92d807427641ec45d28d5907426aa06b4ffd19c5b794729c74d91cd95090e", - "sha256:31d634dea1b47c231b88d384f90605c598214d0c596443c9bb808e11761829f5", - "sha256:5fdfc0bed37315f27d30ae5ae9bad47ec0a0a28c323739d39c8177b7e0929238", - "sha256:6fa8fac14dd5af4819d475f74af12d65fbbfa391d3110c3a972934a5e6507c24", - "sha256:78cc89ebc808886eb190626ee71ab65e47f374121975f86e4d5f7c0e3ce6bed9", - "sha256:7c7ba11720d01cb572b4b6945d115cb103462c0a28996b44d4e540d06e6a90fd", - "sha256:a37ee82f1b8ed4b4645619c504311e71ce845b78f40055e78d71add5fab7da82", - "sha256:aa3ca1f54054e1c6439fdf1edafa2a2b940a9eaac04a7b422a1cba9b2d7b9690", - "sha256:b9de3dd956574662712da8e285f0f54327959a4e95b96a2847d3c3f5ee7b96e2", - "sha256:c0087b428cef9a32d977390656d91b02245e0e91f909870492df7e39202645dd", - "sha256:d87e506ab205799727f0efa34b3888949bf029a3ada5eb000ff632606370ca6e", - "sha256:d8a55585631f9c9eca4b1a996e9732ae023169cf2f46f69e4518d67d96198226", - "sha256:dcb8da8c5ebaa6360c8555547a4c7beb6cd983dd95ba895bb78b86cc8cf3de2b", - "sha256:e2206bb8c17c0f212f1f356d82d72dd090ff4651994034416da9bf0c29732825", - "sha256:e3c57d6579e5bf85f564d6d48d8ee89868b92879a9232b9975d072c346625e92", - "sha256:ef89cbf332b9a735d8a82e9ff79cc743eeeb775ad1cd7100bc2aa2429b496f07", - "sha256:f45c1c3cdda1857bedd4dfe0bbd49c9419af0cc57f33490341edeae97d18f037", - "sha256:fb3c855347310788e4286b867997be354c55535597966ed5dac876d9166013a4" + "sha256:16864152aa6ac346ef83588d6f4f5dc974d27851c034d6970fcb7b6a98bbd318", + "sha256:23dade76fe0194139112eea7ecdfa02ae09924b1d8d853f17f387a356519e484", + "sha256:27d5b83edd245a12dd6b8562569ad3f23e5ffe30cef8cfcc70756dd24b55d12f", + "sha256:2a2a7590b99d872b193cda0592b2c1cd6561159c31b361597c0e69e8926c8d16", + "sha256:46032d4648c74730115f8522effda8ac39bd0385f07edc7aab57b41cc7617933", + "sha256:4c195597d5286d1cc7259aeaeb7e6c1cde07fec9bddf26523eab1b15709291aa", + "sha256:69c971fefb633cfd334ed195d58e76e87f267649f98a2394f7400b178e918936", + "sha256:80b5b68e9c5dda29205ca112e6d5bd647b6b43cf917cfa5ce178d61675291bba", + "sha256:98676d349fdfc17dba9f23b87e9b6a639733d35f5f0ffcccb90e76c8200568f4", + "sha256:9df617736351100879b70d914366b9f9e38aa227885f2590b48badc4a233119d", + "sha256:b2147317b00b20e8d7e01201221af2b278aed449fa436316c42bc63f653e8245", + "sha256:d838ee4562f52793b1b10876e5067cae1a6bb1c3c575091644be9b88cf45d255", + "sha256:db74a92ef9c2a0810e1436d586b3b15d421a39b72f06263358f15c7a609498e0", + "sha256:e100a4ffdeed8c4afac6a5b3f6b4481efe0ad90e0a0ae2d129478abd4bd790bc", + "sha256:e87d88a820050c0e886c9add48eac2f80ff29207a98cca25869a6868c519daa4", + "sha256:fdf017c5b93d58ad77e2690e59322fd09414705c28d69b52fad4a19985422e6c" ], "index": "pypi", - "version": "==4.2.0.34" + "version": "==4.4.0.44" }, "pandas": { "hashes": [ - "sha256:02f1e8f71cd994ed7fcb9a35b6ddddeb4314822a0e09a9c5b2d278f8cb5d4096", - "sha256:13f75fb18486759da3ff40f5345d9dd20e7d78f2a39c5884d013456cec9876f0", - "sha256:35b670b0abcfed7cad76f2834041dcf7ae47fd9b22b63622d67cdc933d79f453", - "sha256:4c73f373b0800eb3062ffd13d4a7a2a6d522792fa6eb204d67a4fad0a40f03dc", - "sha256:5759edf0b686b6f25a5d4a447ea588983a33afc8a0081a0954184a4a87fd0dd7", - "sha256:5a7cf6044467c1356b2b49ef69e50bf4d231e773c3ca0558807cdba56b76820b", - "sha256:69c5d920a0b2a9838e677f78f4dde506b95ea8e4d30da25859db6469ded84fa8", - "sha256:8778a5cc5a8437a561e3276b85367412e10ae9fff07db1eed986e427d9a674f8", - "sha256:9871ef5ee17f388f1cb35f76dc6106d40cb8165c562d573470672f4cdefa59ef", - "sha256:9c31d52f1a7dd2bb4681d9f62646c7aa554f19e8e9addc17e8b1b20011d7522d", - "sha256:ab8173a8efe5418bbe50e43f321994ac6673afc5c7c4839014cf6401bbdd0705", - "sha256:ae961f1f0e270f1e4e2273f6a539b2ea33248e0e3a11ffb479d757918a5e03a9", - "sha256:b3c4f93fcb6e97d993bf87cdd917883b7dab7d20c627699f360a8fb49e9e0b91", - "sha256:c9410ce8a3dee77653bc0684cfa1535a7f9c291663bd7ad79e39f5ab58f67ab3", - "sha256:f69e0f7b7c09f1f612b1f8f59e2df72faa8a6b41c5a436dde5b615aaf948f107", - "sha256:faa42a78d1350b02a7d2f0dbe3c80791cf785663d6997891549d0f86dc49125e" + "sha256:026d764d0b86ee53183aa4c0b90774b6146123eeada4e24946d7d24290777be1", + "sha256:02ec9f5f0b7df7227931a884569ef0b6d32d76789c84bcac1a719dafd1f912e8", + "sha256:08783a33989a6747317766b75be30a594a9764b9f145bb4bcc06e337930d9807", + "sha256:0936991228241db937e87f82ec552a33888dd04a2e0d5a2fa3c689f92fab09e0", + "sha256:188cdfbf8399bc144fa95040536b5ce3429d2eda6c9c8b238c987af7df9f128c", + "sha256:1edf6c254d2d138188e9987159978ee70e23362fe9197f3f100844a197f7e1e4", + "sha256:474fa53e3b2f3a543cbca81f7457bd1f44e7eb1be7171067636307e21b624e9c", + "sha256:59df9f0276aa4854d8bff28c5e5aeb74d9c6bb4d9f55d272b7124a7df40e47d0", + "sha256:9e135ce9929cd0f0ba24f0545936af17ba935f844d4c3a2b979354a73c9440e0", + "sha256:ab6ea0f3116f408a8a59cd50158bfd19d2a024f4e221f14ab1bcd2da4f0c6fdf", + "sha256:b64ffd87a2cfd31b40acd4b92cb72ea9a52a48165aec4c140e78fd69c45d1444", + "sha256:b821f239514a9ce46dd1cd6c9298a03ed58d0235d414ea264aacc1b14916bbe4", + "sha256:c9235b37489168ed6b173551c816b50aa89f03c24a8549a8b4d47d8dc79bfb1e", + "sha256:eb0ac2fd04428f18b547716f70c699a7cc9c65a6947ed8c7e688d96eb91e3db8", + "sha256:eeb64c5b3d4f2ea072ca8afdeb2b946cd681a863382ca79734f1b520b8d2fa26", + "sha256:f7008ec22b92d771b145150978d930a28fab8da3a10131b01bbf39574acdad0b" ], "index": "pypi", - "version": "==1.0.5" + "version": "==1.1.2" }, "pandas-ods-reader": { "hashes": [ @@ -535,66 +551,68 @@ }, "pdftotext": { "hashes": [ - "sha256:d37864049581fb13cdcf7b23d4ea23dac7ca2e9c646e8ecac1a39275ab1cae03" + "sha256:98aeb8b07a4127e1a30223bd933ef080bbd29aa88f801717ca6c5618380b8aa6" ], "index": "pypi", - "version": "==2.1.4" + "version": "==2.1.5" }, "pillow": { "hashes": [ + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117" ], "index": "pypi", "version": "==7.2.0" }, "progressbar2": { "hashes": [ - "sha256:13f228cf357f94cdef933c91c1e771e52e1b1931dbae48267be8fcdc2ae2ce36", - "sha256:27abf038efe5b1b5dd91ecc5f704bc88683c1e2a0b2c0fee04de80a648634a0c" + "sha256:ef72be284e7f2b61ac0894b44165926f13f5d995b2bf3cd8a8dedc6224b255a7", + "sha256:fe2738e7ecb7df52ad76307fe610c460c52b50f5335fd26c3ab80ff7655ba1e0" ], - "version": "==3.51.4" + "version": "==3.53.1" }, "psutil": { "hashes": [ - "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058", - "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953", - "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4", - "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e", - "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f", - "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38", - "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e", - "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8", - "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26", - "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5", - "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310" + "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8", + "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498", + "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6", + "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c", + "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195", + "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f", + "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb", + "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1", + "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf", + "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2", + "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==5.7.0" + "version": "==5.7.2" }, "pybgpranking": { "editable": true, @@ -612,72 +630,82 @@ }, "pycryptodome": { "hashes": [ - "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", - "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", - "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", - "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", - "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", - "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", - "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", - "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", - "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", - "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", - "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", - "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", - "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", - "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", - "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", - "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", - "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", - "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", + "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", + "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94", "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", - "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", - "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", - "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", - "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", - "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", - "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", - "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", - "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", + "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", + "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", + "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", + "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", + "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", - "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94" + "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", + "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", + "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", + "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", + "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", + "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", + "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", + "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", + "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", + "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f", + "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", + "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", + "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", + "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", + "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", + "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", + "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e", + "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", + "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", + "sha256:663f8de2b3df2e744d6e1610506e0ea4e213bde906795953c1e82279c169f0a7", + "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd", + "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", + "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", + "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", + "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" }, "pycryptodomex": { "hashes": [ - "sha256:06f5a458624c9b0e04c0086c7f84bcc578567dab0ddc816e0476b3057b18339f", - "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", - "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", - "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", - "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", - "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", - "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", - "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", - "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", - "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", - "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", - "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", - "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", - "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", - "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", - "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", - "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", - "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", - "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", - "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", + "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", + "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", + "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", + "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", + "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", + "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", + "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", + "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", + "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", + "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", + "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", + "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", + "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", + "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", + "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", + "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060", - "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", + "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb", + "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", + "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", + "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", - "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb" + "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", + "sha256:85c108b42e47d4073344ff61d4e019f1d95bb7725ca0fe87d0a2deb237c10e49", + "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", + "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", + "sha256:06f5a458624c9b0e04c0086c7f84bcc578567dab0ddc816e0476b3057b18339f" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" @@ -728,7 +756,7 @@ "pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "ec28820cf491ca7d385477996afa0547eb6b6830" + "ref": "77e7111c29f935f5dc4790c7493a3cc91f79131e" }, "pyonyphe": { "editable": true, @@ -767,16 +795,23 @@ }, "pyrsistent": { "hashes": [ - "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" + "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" ], - "version": "==0.16.0" + "markers": "python_version >= '3.5'", + "version": "==0.17.3" }, "pytesseract": { "hashes": [ - "sha256:afd8a5cdf8ab5d35690efbe71cbf5f89419f668ea8dde7649149815d5c5a899a" + "sha256:b79641b7915ff039da22d5591cb2f5ca6cb0ed7c65194c9c750360dc6a1cc87f" ], "index": "pypi", - "version": "==0.3.4" + "version": "==0.3.6" + }, + "python-baseconv": { + "hashes": [ + "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b" + ], + "version": "==1.2.2" }, "python-dateutil": { "hashes": [ @@ -793,6 +828,13 @@ "index": "pypi", "version": "==0.8.10" }, + "python-engineio": { + "hashes": [ + "sha256:36b33c6aa702d9b6a7f527eec6387a2da1a9a24484ec2f086d76576413cef04b", + "sha256:cfded18156862f94544a9f8ef37f56727df731c8552d7023f5afee8369be2db6" + ], + "version": "==3.13.2" + }, "python-magic": { "hashes": [ "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", @@ -807,6 +849,16 @@ "index": "pypi", "version": "==0.6.18" }, + "python-socketio": { + "extras": [ + "client" + ], + "hashes": [ + "sha256:358d8fbbc029c4538ea25bcaa283e47f375be0017fcba829de8a3a731c9df25a", + "sha256:d437f797c44b6efba2f201867cf02b8c96b97dff26d4e4281ac08b45817cd522" + ], + "version": "==4.6.0" + }, "python-utils": { "hashes": [ "sha256:ebaadab29d0cb9dca0a82eab9c405f5be5125dbbff35b8f32cc433fa498dbaa7", @@ -871,49 +923,49 @@ }, "reportlab": { "hashes": [ - "sha256:0f0c2d98e213d51ae527c0301364d3376cb05f6c47251368a9abd4c3197fcefa", - "sha256:1425c7ea60b8691a881ae21ea0f6907a1dc480d84204ccbfea6da41fbee8f594", - "sha256:204f1d245875ab3d076b37c1a18ac8d2e3222842e13cfa282bcd95282be239e5", - "sha256:21627b57249303bf9b5a633099d058ae9f8625fd6f90cfe79348c48fd5a242cd", - "sha256:2e8e3242f80b79f2470f1b5979abbdb41f31b1333543b830749100342f837d40", - "sha256:2eced06dec3f36135c626b9823649ef9cac95c5634d1bc743a15ee470027483b", - "sha256:3472aa0b74a3b2f252dce823f3c3ba6af8a24de0c1729441deaaf50bed6de9f9", - "sha256:3f0353ffefd3afc0061f4794ef608d6c6f32e69816885f4d45c625c20d8eaf5b", - "sha256:4a9f4540a8eddf56d900ceeb8136bd0ca866c208ba3dcbcde73f07405dbadfba", - "sha256:4eea1afb4aa89780734f44175508edff82928fdf460c9bd60bc719dd99041dc3", - "sha256:5803ffebd36de1ada417f50ce65d379ea5a0bf1a2e8f5d5710a031b3b349b726", - "sha256:58f5f72fc8e5932dedcf24789908a81c6b1e13ea4d63bd9a9a39dc698d8c3321", - "sha256:5b588e5f251c76a8d3589023d1c369c7968e0efe2b38ad5948f665edbf6f9e8b", - "sha256:5d922768fe11a58d80694852aba7389d613c15eb1871c5581a2f075996873d57", - "sha256:5d98f297c5cdd5bc0ccf5697c20b03602ee3378c97938d20312662b27cd9a1d6", - "sha256:66d1d96e97a562614943ecb9daf438e392b3d0b033bd5f4a8098ab616dd877da", - "sha256:670650970c7ba7164cf6340bcd182e7e933eff5d65183af98ee77b40cc25a438", - "sha256:67bb95af7bc8ad7925d299f310d15d556d3e7026fe1b60d8e290454604ae0a85", - "sha256:9c999f5d1a600c4970ba293789b6da14e02e3763a8d3d9abe42dcafa8a5318e9", - "sha256:9d62bef5347063a984e63410fa5a69f1d2cc2fdf8d6ed3d0b9d4ea2ccb4b4154", - "sha256:a14a0d603727b6be2e549c52dd42678ab2d06d2721d4580199e3161843e59298", - "sha256:a3a17b46ff1a15eb29370e11796d8914ef4ea67471bdbc4aa9a9eb9284f4e44c", - "sha256:a6d3e20beeba3fd68cec73b8c0785bfa648c06ac76d1f142c60ccb1a8d2506b6", - "sha256:ad7d7003c732f2be42580e3906e92bd9d2aca5e098898c597554be9ca627fad5", - "sha256:af0ee7b50b85543b68b043e61271963ff5671e564e1d620a404c24a24d4f537c", - "sha256:b3eec55274f5ead7e3af2bf0c01b481ffe1b4c6a7dae42b63d85543e9f2f9a0f", - "sha256:b48c21d43a7ab956954591ce3f71db92ce542bb7428db09734425e2b77ac3142", - "sha256:b761905ab85beb79cf7929c9a019f30ad65664e5733d57a30a995e7b9bef06d1", - "sha256:bbae2f054d0f234c3382076efa337802997aca0f3f664e314f65eefb9d694fa9", - "sha256:bd4157d0bc40fb72bb676fc745fdd648022cccaf4ccfbb291af7f48831d0d5d9", - "sha256:bf74cfabf332034f42a54938eb335543cbf92790170300dbe236ba83b7601cd0", - "sha256:c253c8571db2df3886e390a2bfbe917222953054f4643437373b824f64b013cd", - "sha256:ce1277a6acbc62e9966f410f2596ac533ee0cd5df9b69d5fe4406338a169b7d8", - "sha256:ce8f56987e0e456063e311f066a81496b8b9626c846f2cb0ebb554d1a5f40839", - "sha256:d6264a0589ba8032d9c3bdca9a3e87a897ede09b7f6a8ad5e83b57573212e01e", - "sha256:e6fa0c97e3929d00db27e8cf3b2b5771e94f5f179086c4b0e3213dff53637372", - "sha256:f0930f2b6dddd477b3331ec670171a4662336aac1a778e1a30e980a5cbf40b17", - "sha256:f8cb2b4b925ca6b6e4fdefd288a707776ac686c45034f34d4c952f122d11c40b", - "sha256:f9b71539f518323d95850405c49c01fc3d2f0f0b9f3e157de6d2786804fb28a4", - "sha256:fc488e661f99c915362e0373218f8727cecf888eb1b0eb3a8fe1af624a1b9776" + "sha256:0734f63685038e8c9dc176fa44676c660f347420b78abe1be81245c8ca48fa0d", + "sha256:0738973406752c9e36c066ca453b4332ceae110294e8640d2a9b3846c6c381dc", + "sha256:09238d17bc97d63289b9adc2e1c16699f54985bfaf6ca7c681a2b92edda3e577", + "sha256:124b0d449f33464d9b2132d18574d8757b613bea4090a34a99381037e7ec781d", + "sha256:15e696e490432aea873fe01c042b83f9df87a5cc0c225cd8240dfa80d383466d", + "sha256:24c55a6d8229186ebf3587d4d81ddb1b4261975f2cfeef6b8fcc259cbde49a68", + "sha256:26c410c6f9b9ccda0b7dc827a494fdff1c87267b7f506e2a6ec8d7ae6f41317d", + "sha256:35285ab45b9c5575ebe3642fc772450aa04886713e69270ef80e6589e3232d1f", + "sha256:4441bed8cd679c4769624a9349c31880a0769a925b5114ff79a43cac2ec25b78", + "sha256:52e60b52180006da54510ba8a153f278490d6d4a65c63f5240a9e788ca2db2a0", + "sha256:5b4c7329340bef4ee2d773ef48d022a37f6dacfeda5fa1834827d2bc83df53fe", + "sha256:62a8b80af9b479277f6a10db62c60b2c2d3e2e28958ad851521efbbd5659c179", + "sha256:67525ae908f0c9812f6bf33578805f5e50689a4ba3a0bc9730338327727b4b2a", + "sha256:6e49e1822c0ee2459ac358fb29b36968d839f6dcc2fd6790cd73c4527ce42b7f", + "sha256:6edd891881e2536c420fcda443ac693d349a27a739a79dc9d75cc80a9854be12", + "sha256:726c2ddf51a43c1025979f1a3e27b24d441f9cd4a017887c21321c48d401308d", + "sha256:7357c7286e638ac9ee572d0b86fbd84d809879ca0231112aa7cece18f9fd5b73", + "sha256:7c0ecfee57321dbf25e0a2957056aa24b57c9334ad5e6e044b512a22040967ef", + "sha256:7ea16cab0ca2a1373a78e3f723ebb53b6f8bd17b406395f3168b2549861bf763", + "sha256:7f283fdcd0417a43431886cb4b9adeb3c0dce6b8e701b8258d6ef73e323ee105", + "sha256:847164d15142652a3f11f9101603878136c6a2f663460046d08421c039f6bc7d", + "sha256:8499aeb496b6ff6df0602c1395b201d1b40b96d43d29e5c0805c81b55f333ef6", + "sha256:9894bb2f41950f2cb2d84a46b39d487fc0f8f083730bfe2120be2743b7145fd0", + "sha256:a4f3eec7985642f139cb9b522e5dd1581186cf9ee016853d4b3b5abf2e5b8bc4", + "sha256:a5ad4696987fdb42d976093e1b1a585ec2ad42634b5ce94dbb71362dcc758afb", + "sha256:a85b0c694104f5e45ede8172c41ba6593554df3db6c50131bb6b7d7a0866d633", + "sha256:a8d5bc7413a6b23c8023e2daac879986f35fd55c94ee69f5e94de2afe1a6b40e", + "sha256:aeaa2d1c8e4227ac80ab6768cbb7b807b2185581cb8be52026ba87289f4f8afd", + "sha256:b4ea035fccca8d98e2106d0d18c11bf58c1c75179c949be6ac169c362543520d", + "sha256:b8f4ba3560ced143835c65d18c114070dbd9caa9c7250554319f23c3caa63528", + "sha256:ba9bcadc5d8ea45d2b009ccff56d89f40c29b0daa373142fa758c892f1699b3d", + "sha256:bfde3244b5df701ccc7423d7ad8b0b5b138ee84a5aa523a25a4d22f801db7e34", + "sha256:c8ad3619d18b61ab024d519f3e95fac412259b4a6b074aef1b702755ebd6de38", + "sha256:caea1f634b89b19d9a7a474b92ce0c330aef51e8a9e57e193d77e206da4f1534", + "sha256:d6ef866c7783b3fedcbd4396617e18cb947349e99d04c7253dfaab0bc38f49e4", + "sha256:d853bffa2394bf8da27c0f94fe3121830ebd418e911f09b074d8b08b909ec5e4", + "sha256:d9ef21229dc82bf76f75858ee02185dd5522abd2a175c9a5cff3add9426cf98b", + "sha256:dc1f231ae768baf88bf462a6736e9091606c3326d3b242d22e6df6f0dd9553fc", + "sha256:de271298f61ce674354826b2a64f932a4f41317460be610909c213ebc44ab988", + "sha256:e28065627fcbed9978d6d1f9bf68b0bec5d7fa40a7ab64bda3149590d4022f91" ], "index": "pypi", - "version": "==3.5.44" + "version": "==3.5.52" }, "requests": { "extras": [ @@ -935,18 +987,18 @@ }, "shodan": { "hashes": [ - "sha256:31b0740ffaf7c5196a26a0b1edf7d271dffe54ea52bb1b34ba87aa231b5c339b" + "sha256:d2d37d47dd084747df672e6d981f6d72d5d03f4ee12f0ce2170e618147578349" ], "index": "pypi", - "version": "==1.23.0" + "version": "==1.23.1" }, "sigmatools": { "hashes": [ - "sha256:5453717e452aa7860c5e6ac80bcee4f398d70956fc2ee9859bc7255067da8736", - "sha256:cdfeb8200c09c0a40ea1a015e57f3b8e2ba62a28352ca05fa015674f640871e3" + "sha256:5cca698e11f9f3f2f80b92cb4873f9958898ad23d26ce78ee4a573777f4f2035", + "sha256:719c6c19ff60177f3a155d0dd2b054a4ad7e906dec3e88dae668c2b2d200f82c" ], "index": "pypi", - "version": "==0.17.0" + "version": "==0.18.1" }, "six": { "hashes": [ @@ -956,18 +1008,26 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, + "socialscan": { + "hashes": [ + "sha256:688ef0ae170f3270f541c0fdfaa859bad1fe56955c692fdd3b312deed8a16362", + "sha256:6db737865f08f5a6745fb8ccd001ef13856b19b887da1c8dcd480544a3d47e92" + ], + "index": "pypi", + "version": "==1.3.0" + }, "socketio-client": { "hashes": [ - "sha256:540d8ab209154d1d9cdb97c170c589a14f7d7f17e19c14e2f59f0307e6175485" + "sha256:ef2e362a85ef2816fb224d727319c4b743d63b4dd9e1da99c622c9643fc4e2a0" ], - "version": "==0.5.6" + "version": "==0.5.7.4" }, "soupsieve": { "hashes": [ "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55", "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232" ], - "markers": "python_version >= '3.5'", + "markers": "python_version >= '3.0'", "version": "==2.0.1" }, "sparqlwrapper": { @@ -983,11 +1043,11 @@ }, "stix2-patterns": { "hashes": [ - "sha256:587a82545680311431e5610036dd6c8c247347a24243fafdafaae2df4d6d7799", - "sha256:7fcb2fa67efeac2a8c493d367c93d0ce6243a10e2eff715ae9f2983e6b32b95d" + "sha256:373a3de163e1b146499c6e5a7908e1f0987173139480897728fcbbba6a806f95", + "sha256:5a38f634adc856b7d03e13dd140d38e184ac1ef11077c1ffca28a262fa6d8c41" ], "index": "pypi", - "version": "==1.3.0" + "version": "==1.3.1" }, "tabulate": { "hashes": [ @@ -1011,12 +1071,20 @@ "markers": "python_version >= '3.5'", "version": "==6.0.4" }, + "tqdm": { + "hashes": [ + "sha256:2dd75fdb764f673b8187643496fcfbeac38348015b665878e582b152f3391cdb", + "sha256:93b7a6a9129fce904f6df4cf3ae7ff431d779be681a95c3344c26f3e6c09abfa" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==4.50.0" + }, "trustar": { "hashes": [ - "sha256:73336b94012427b66ee61db65fc3c2cea2ed743beaa56cdd5a4c1674ef1a7660" + "sha256:47c45674a4a310dc8d932035e0de112de55c1e899663865b996a6b6b2d79cbde" ], "index": "pypi", - "version": "==0.3.29" + "version": "==0.3.33" }, "tzlocal": { "hashes": [ @@ -1048,11 +1116,11 @@ }, "urllib3": { "hashes": [ - "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", - "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.9" + "version": "==1.25.10" }, "uwhois": { "editable": true, @@ -1076,20 +1144,20 @@ }, "vulners": { "hashes": [ - "sha256:00ff8744d07f398880afc1efcab6dac4abb614c84553fa31b2d439f986b8e0db", - "sha256:90a855915b4fb4dbd0325643d9e643602975fcb931162e5dc2e7778d1daa2fd8", - "sha256:f230bfcd42663326b7c9b8fa117752e26cad4ccca528caaab531c5b592af8cb5" + "sha256:4e78fc7492d33a1e612e7d5046e51f4c272eb7febdfc0fc06061648d2153e75a", + "sha256:6b088b7c8da9bdcc16e8283afd4a8f804388f1432d12d17b29b770778113ec62", + "sha256:7964884c1f262004a950d5915d49520d22afa3ab175d473492e2dbcc6b5e0a9a" ], "index": "pypi", - "version": "==1.5.5" + "version": "==1.5.8" }, "wand": { "hashes": [ - "sha256:d5b75ac13d7485032970926415648586eafeeb1eb62ed6ebd0778358cf9d70e0", - "sha256:df0780b1b54938a43d29279a6588fde11e349550c8958a673d57c26a3e6de7f1" + "sha256:566b3d049858efa879096a7ab2e0516d67a240e6c3ffd7a267298c41e81c14b7", + "sha256:d21429288fe0de63d829dbbfb26736ebaed9fd0792c2a0dc5943c5cab803a708" ], "index": "pypi", - "version": "==0.6.1" + "version": "==0.6.3" }, "websocket-client": { "hashes": [ @@ -1114,10 +1182,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:828b3285fc95105f5b1946a6a015b31cf388bd5378fdc6604e4d1b7839df2e77", - "sha256:82a3b0e73e3913483da23791d1a25e4d2dbb3837d1be4129473526b9a270a5cc" + "sha256:99b665203d737db31378ec729c9990a004c1abae53a6fa211c104f8c2e36cffd", + "sha256:b89002dea57bb3d4c8207f3e28ef8244bfd9e936b85d74e7dd1f97e11bb70313" ], - "version": "==1.2.9" + "version": "==1.3.6" }, "yara-python": { "hashes": [ @@ -1138,36 +1206,36 @@ }, "yarl": { "hashes": [ - "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce", - "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6", - "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce", - "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae", - "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d", - "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f", - "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b", - "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b", - "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb", - "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462", - "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea", - "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70", - "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1", - "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a", - "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b", - "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080", - "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" + "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e", + "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5", + "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580", + "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc", + "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b", + "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2", + "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a", + "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921", + "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e", + "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1", + "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d", + "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131", + "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a", + "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1", + "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188", + "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020", + "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a" ], "markers": "python_version >= '3.5'", - "version": "==1.4.2" + "version": "==1.6.0" } }, "develop": { "attrs": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", + "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==20.2.0" }, "certifi": { "hashes": [ @@ -1185,49 +1253,52 @@ }, "codecov": { "hashes": [ - "sha256:491938ad774ea94a963d5d16354c7299e90422a33a353ba0d38d0943ed1d5091", - "sha256:b67bb8029e8340a7bf22c71cbece5bd18c96261fdebc2f105ee4d5a005bc8728", - "sha256:d8b8109f44edad03b24f5f189dac8de9b1e3dc3c791fa37eeaf8c7381503ec34" + "sha256:24545847177a893716b3455ac5bfbafe0465f38d4eb86ea922c09adc7f327e65", + "sha256:355fc7e0c0b8a133045f0d6089bde351c845e7b52b99fec5903b4ea3ab5f6aab", + "sha256:7877f68effde3c2baadcff807a5d13f01019a337f9596eece0d64e57393adf3a" ], "index": "pypi", - "version": "==2.1.7" + "version": "==2.1.9" }, "coverage": { "hashes": [ - "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", - "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", - "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", - "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", - "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", - "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", - "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", - "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", - "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", - "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", - "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", - "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", - "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", - "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", - "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", - "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", - "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", - "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", - "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", - "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", - "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", - "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", - "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", - "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", - "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", - "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", - "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", - "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", - "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", - "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", - "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" + "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516", + "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259", + "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9", + "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097", + "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0", + "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f", + "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7", + "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c", + "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5", + "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7", + "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729", + "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978", + "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9", + "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f", + "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9", + "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822", + "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418", + "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82", + "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f", + "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d", + "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221", + "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4", + "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21", + "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709", + "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54", + "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d", + "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270", + "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24", + "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751", + "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a", + "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237", + "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7", + "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636", + "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==5.1" + "version": "==5.3" }, "flake8": { "hashes": [ @@ -1245,6 +1316,13 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.10" }, + "iniconfig": { + "hashes": [ + "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437", + "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69" + ], + "version": "==1.0.1" + }, "mccabe": { "hashes": [ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -1252,14 +1330,6 @@ ], "version": "==0.6.1" }, - "more-itertools": { - "hashes": [ - "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5", - "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2" - ], - "markers": "python_version >= '3.5'", - "version": "==8.4.0" - }, "nose": { "hashes": [ "sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac", @@ -1319,11 +1389,11 @@ }, "pytest": { "hashes": [ - "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", - "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8" + "sha256:1cd09785c0a50f9af72220dd12aa78cfa49cbffc356c61eab009ca189e018a33", + "sha256:d010e24666435b39a4cf48740b039885642b6c273a3f77be3e7e03554d2806b7" ], "index": "pypi", - "version": "==5.4.3" + "version": "==6.1.0" }, "requests": { "extras": [ @@ -1344,20 +1414,20 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, + "toml": { + "hashes": [ + "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", + "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + ], + "version": "==0.10.1" + }, "urllib3": { "hashes": [ - "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", - "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.9" - }, - "wcwidth": { - "hashes": [ - "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", - "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" - ], - "version": "==0.2.5" + "version": "==1.25.10" } } } From d950b4d7ec80f5dec748ab71e69d2063a8762168 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 2 Oct 2020 01:50:49 +0200 Subject: [PATCH 190/220] fix: Removed debugging print command --- misp_modules/modules/expansion/socialscan.py | 1 - 1 file changed, 1 deletion(-) diff --git a/misp_modules/modules/expansion/socialscan.py b/misp_modules/modules/expansion/socialscan.py index 7c59a35..da30e4c 100644 --- a/misp_modules/modules/expansion/socialscan.py +++ b/misp_modules/modules/expansion/socialscan.py @@ -61,7 +61,6 @@ def parse_results(query_results, feature): 'values': result } for result in results ] - print(json.dumps(to_return, indent=4)) return {'results': to_return} From 0072e04627f3dc33db0412598ac809dcaeec3f4b Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 2 Oct 2020 16:41:47 +0200 Subject: [PATCH 191/220] chg: Updated expansion modules documentation - Added documentation for the missing modules - Renamed some of the documentation files to match with the module names and avoid issues within the documentation file (README.md) with the link of the miss-spelled module names --- doc/README.md | 155 +++++++++++++++++- doc/expansion/cve_advanced.json | 8 + .../{docx-enrich.json => docx_enrich.json} | 0 doc/expansion/geoip_asn.json | 9 + doc/expansion/geoip_city.json | 9 + doc/expansion/google_search.json | 9 + doc/expansion/intel471.json | 9 + .../{ocr-enrich.json => ocr_enrich.json} | 0 .../{ods-enrich.json => ods_enrich.json} | 0 .../{odt-enrich.json => odt_enrich.json} | 0 .../{pdf-enrich.json => pdf_enrich.json} | 0 .../{pptx-enrich.json => pptx_enrich.json} | 0 doc/expansion/ransomcoindb.json | 8 + doc/expansion/sophoslabs_intelix.json | 9 + .../{xlsx-enrich.json => xlsx_enrich.json} | 0 doc/logos/google.png | Bin 0 -> 15903 bytes doc/logos/intel471.png | Bin 0 -> 6713 bytes doc/logos/sophoslabs_intelix.svg | 32 ++++ 18 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 doc/expansion/cve_advanced.json rename doc/expansion/{docx-enrich.json => docx_enrich.json} (100%) create mode 100644 doc/expansion/geoip_asn.json create mode 100644 doc/expansion/geoip_city.json create mode 100644 doc/expansion/google_search.json create mode 100644 doc/expansion/intel471.json rename doc/expansion/{ocr-enrich.json => ocr_enrich.json} (100%) rename doc/expansion/{ods-enrich.json => ods_enrich.json} (100%) rename doc/expansion/{odt-enrich.json => odt_enrich.json} (100%) rename doc/expansion/{pdf-enrich.json => pdf_enrich.json} (100%) rename doc/expansion/{pptx-enrich.json => pptx_enrich.json} (100%) create mode 100644 doc/expansion/ransomcoindb.json create mode 100644 doc/expansion/sophoslabs_intelix.json rename doc/expansion/{xlsx-enrich.json => xlsx_enrich.json} (100%) create mode 100644 doc/logos/google.png create mode 100644 doc/logos/intel471.png create mode 100644 doc/logos/sophoslabs_intelix.svg diff --git a/doc/README.md b/doc/README.md index 6469dd0..1225780 100644 --- a/doc/README.md +++ b/doc/README.md @@ -311,6 +311,26 @@ An expansion hover module to expand information about CVE id. ----- +#### [cve_advanced](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cve_advanced.py) + + + +An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE). +- **features**: +>The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to gather additional information. +> +>The result of the query is then parsed to return additional information about the vulnerability, like its cvss score or some references, as well as the potential related weaknesses and attack patterns. +> +>The vulnerability additional data is returned in a vulnerability MISP object, and the related additional information are put into weakness and attack-pattern MISP objects. +- **input**: +>Vulnerability attribute. +- **output**: +>Additional information about the vulnerability, such as its cvss score, some references, or the related weaknesses and attack patterns. +- **references**: +>https://cve.circl.lu, https://cve/mitre.org/ + +----- + #### [cytomic_orion](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cytomic_orion.py) @@ -369,7 +389,7 @@ A simple DNS expansion service to resolve IP address from domain MISP attributes ----- -#### [docx-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/docx-enrich.py) +#### [docx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/docx_enrich.py) @@ -476,6 +496,42 @@ Module to access Farsight DNSDB Passive DNS. ----- +#### [geoip_asn](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_asn.py) + + +- **descrption**: +>An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about its related AS number. +- **features**: +>The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the related AS number. +- **input**: +>An IP address MISP attribute. +- **output**: +>Text containing information about the AS number of the IP address. +- **references**: +>https://www.maxmind.com/en/home +- **requirements**: +>A local copy of Maxmind's Geolite database + +----- + +#### [geoip_city](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_city.py) + + + +An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about the city where it is located. +- **features**: +>The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the city where this IP address is located. +- **input**: +>An IP address MISP attribute. +- **output**: +>Text containing information about the city where the IP address is located. +- **references**: +>https://www.maxmind.com/en/home +- **requirements**: +>A local copy of Maxmind's Geolite database + +----- + #### [geoip_country](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_country.py) @@ -496,6 +552,24 @@ Module to query a local copy of Maxmind's Geolite database. ----- +#### [google_search](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/google_search.py) + + +- **descrption**: +>A hover module to get information about an url using a Google search. +- **features**: +>The module takes an url as input to query the Google search API. The result of the query is then return as raw text. +- **input**: +>An url attribute. +- **output**: +>Text containing the result of a Google search on the input url. +- **references**: +>https://github.com/abenassi/Google-Search-API +- **requirements**: +>The python Google Search API library + +----- + #### [greynoise](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/greynoise.py) @@ -544,6 +618,37 @@ Module to access haveibeenpwned.com API. ----- +#### [intel471](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/intel471.py) + + +- **descrption**: +>An expansion module to query Intel471 in order to get additional information about a domain, ip address, email address, url or hash. +- **features**: +>The module uses the Intel471 python library to query the Intel471 API with the value of the input attribute. The result of the query is then returned as freetext so the Freetext import parses it. +- **input**: +>A MISP attribute whose type is included in the following list: +>- hostname +>- domain +>- url +>- ip-src +>- ip-dst +>- email-src +>- email-dst +>- target-email +>- whois-registrant-email +>- whois-registrant-name +>- md5 +>- sha1 +>- sha256 +- **output**: +>Freetext +- **references**: +>https://public.intel471.com/ +- **requirements**: +>The intel471 python library + +----- + #### [intelmq_eventdb](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/intelmq_eventdb.py) @@ -733,7 +838,7 @@ Query the MALWAREbazaar API to get additional information about the input hash a ----- -#### [ocr-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ocr-enrich.py) +#### [ocr_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ocr_enrich.py) Module to process some optical character recognition on pictures. - **features**: @@ -747,7 +852,7 @@ Module to process some optical character recognition on pictures. ----- -#### [ods-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ods-enrich.py) +#### [ods_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ods_enrich.py) @@ -763,7 +868,7 @@ Module to extract freetext from a .ods document. ----- -#### [odt-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/odt-enrich.py) +#### [odt_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/odt_enrich.py) @@ -902,7 +1007,7 @@ Module to get information from AlienVault OTX. ----- -#### [pdf-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pdf-enrich.py) +#### [pdf_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pdf_enrich.py) @@ -918,7 +1023,7 @@ Module to extract freetext from a PDF document. ----- -#### [pptx-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pptx-enrich.py) +#### [pptx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pptx_enrich.py) @@ -948,6 +1053,24 @@ Module to decode QR codes. ----- +#### [ransomcoindb](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ransomcoindb.py) +- **descrption**: +>Module to access the ransomcoinDB with a hash or btc address attribute and get the associated btc address of hashes. +- **features**: +>The module takes either a hash attribute or a btc attribute as input to query the ransomcoinDB API for some additional data. +> +>If the input is a btc address, we will get the associated hashes returned in a file MISP object. If we query ransomcoinDB with a hash, the response contains the associated btc addresses returned as single MISP btc attributes. +- **input**: +>A hash (md5, sha1 or sha256) or btc attribute. +- **output**: +>Hashes associated to a btc address or btc addresses associated to a hash. +- **references**: +>https://ransomcoindb.concinnity-risks.com +- **requirements**: +>A ransomcoinDB API key. + +----- + #### [rbl](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/rbl.py) Module to check an IPv4 address against known RBLs. @@ -1091,6 +1214,24 @@ An expansion hover module to perform a syntax check on sigma rules. ----- +#### [sophoslabs_intelix](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sophoslabs_intelix.py) + + + +An expansion module to query the Sophoslabs intelix API to get additional information about an ip address, url, domain or sha256 attribute. +- **features**: +>The module takes an ip address, url, domain or sha256 attribute and queries the SophosLabs Intelix API with the attribute value. The result of this query is a SophosLabs Intelix hash report, or an ip or url lookup, that is then parsed and returned in a MISP object. +- **input**: +>An ip address, url, domain or sha256 attribute. +- **output**: +>SophosLabs Intelix report and lookup objects +- **references**: +>https://aws.amazon.com/marketplace/pp/B07SLZPMCS +- **requirements**: +>A client_id and client_secret pair to authenticate to the SophosLabs Intelix API + +----- + #### [sourcecache](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sourcecache.py) Module to cache web pages of analysis reports, OSINT sources. The module returns a link of the cached page. @@ -1442,7 +1583,7 @@ An expansion module for IBM X-Force Exchange. ----- -#### [xlsx-enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/xlsx-enrich.py) +#### [xlsx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/xlsx_enrich.py) diff --git a/doc/expansion/cve_advanced.json b/doc/expansion/cve_advanced.json new file mode 100644 index 0000000..a4b2ac6 --- /dev/null +++ b/doc/expansion/cve_advanced.json @@ -0,0 +1,8 @@ +{ + "description": "An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE).", + "logo": "logos/cve.png", + "input": "Vulnerability attribute.", + "output": "Additional information about the vulnerability, such as its cvss score, some references, or the related weaknesses and attack patterns.", + "references": ["https://cve.circl.lu", "https://cve/mitre.org/"], + "features": "The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to gather additional information.\n\nThe result of the query is then parsed to return additional information about the vulnerability, like its cvss score or some references, as well as the potential related weaknesses and attack patterns.\n\nThe vulnerability additional data is returned in a vulnerability MISP object, and the related additional information are put into weakness and attack-pattern MISP objects." +} diff --git a/doc/expansion/docx-enrich.json b/doc/expansion/docx_enrich.json similarity index 100% rename from doc/expansion/docx-enrich.json rename to doc/expansion/docx_enrich.json diff --git a/doc/expansion/geoip_asn.json b/doc/expansion/geoip_asn.json new file mode 100644 index 0000000..98189c7 --- /dev/null +++ b/doc/expansion/geoip_asn.json @@ -0,0 +1,9 @@ +{ + "descrption": "An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about its related AS number.", + "logo": "logos/maxmind.png", + "requirements": ["A local copy of Maxmind's Geolite database"], + "input": "An IP address MISP attribute.", + "output": "Text containing information about the AS number of the IP address.", + "references": ["https://www.maxmind.com/en/home"], + "features": "The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the related AS number." +} diff --git a/doc/expansion/geoip_city.json b/doc/expansion/geoip_city.json new file mode 100644 index 0000000..bf6d8fa --- /dev/null +++ b/doc/expansion/geoip_city.json @@ -0,0 +1,9 @@ +{ + "description": "An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about the city where it is located.", + "logo": "logos/maxmind.png", + "requirements": ["A local copy of Maxmind's Geolite database"], + "input": "An IP address MISP attribute.", + "output": "Text containing information about the city where the IP address is located.", + "references": ["https://www.maxmind.com/en/home"], + "features": "The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the city where this IP address is located." +} diff --git a/doc/expansion/google_search.json b/doc/expansion/google_search.json new file mode 100644 index 0000000..a3caddf --- /dev/null +++ b/doc/expansion/google_search.json @@ -0,0 +1,9 @@ +{ + "descrption": "A hover module to get information about an url using a Google search.", + "logo": "logos/google.png", + "requirements": ["The python Google Search API library"], + "input": "An url attribute.", + "output": "Text containing the result of a Google search on the input url.", + "references": ["https://github.com/abenassi/Google-Search-API"], + "features": "The module takes an url as input to query the Google search API. The result of the query is then return as raw text." +} diff --git a/doc/expansion/intel471.json b/doc/expansion/intel471.json new file mode 100644 index 0000000..72dbaba --- /dev/null +++ b/doc/expansion/intel471.json @@ -0,0 +1,9 @@ +{ + "descrption": "An expansion module to query Intel471 in order to get additional information about a domain, ip address, email address, url or hash.", + "logo": "logos/intel471.png", + "requirements": ["The intel471 python library"], + "input": "A MISP attribute whose type is included in the following list:\n- hostname\n- domain\n- url\n- ip-src\n- ip-dst\n- email-src\n- email-dst\n- target-email\n- whois-registrant-email\n- whois-registrant-name\n- md5\n- sha1\n- sha256", + "output": "Freetext", + "references": ["https://public.intel471.com/"], + "features": "The module uses the Intel471 python library to query the Intel471 API with the value of the input attribute. The result of the query is then returned as freetext so the Freetext import parses it." +} diff --git a/doc/expansion/ocr-enrich.json b/doc/expansion/ocr_enrich.json similarity index 100% rename from doc/expansion/ocr-enrich.json rename to doc/expansion/ocr_enrich.json diff --git a/doc/expansion/ods-enrich.json b/doc/expansion/ods_enrich.json similarity index 100% rename from doc/expansion/ods-enrich.json rename to doc/expansion/ods_enrich.json diff --git a/doc/expansion/odt-enrich.json b/doc/expansion/odt_enrich.json similarity index 100% rename from doc/expansion/odt-enrich.json rename to doc/expansion/odt_enrich.json diff --git a/doc/expansion/pdf-enrich.json b/doc/expansion/pdf_enrich.json similarity index 100% rename from doc/expansion/pdf-enrich.json rename to doc/expansion/pdf_enrich.json diff --git a/doc/expansion/pptx-enrich.json b/doc/expansion/pptx_enrich.json similarity index 100% rename from doc/expansion/pptx-enrich.json rename to doc/expansion/pptx_enrich.json diff --git a/doc/expansion/ransomcoindb.json b/doc/expansion/ransomcoindb.json new file mode 100644 index 0000000..bc4e2ab --- /dev/null +++ b/doc/expansion/ransomcoindb.json @@ -0,0 +1,8 @@ +{ + "descrption": "Module to access the ransomcoinDB with a hash or btc address attribute and get the associated btc address of hashes.", + "requirements": ["A ransomcoinDB API key."], + "input": "A hash (md5, sha1 or sha256) or btc attribute.", + "output": "Hashes associated to a btc address or btc addresses associated to a hash.", + "references": ["https://ransomcoindb.concinnity-risks.com"], + "features": "The module takes either a hash attribute or a btc attribute as input to query the ransomcoinDB API for some additional data.\n\nIf the input is a btc address, we will get the associated hashes returned in a file MISP object. If we query ransomcoinDB with a hash, the response contains the associated btc addresses returned as single MISP btc attributes." +} diff --git a/doc/expansion/sophoslabs_intelix.json b/doc/expansion/sophoslabs_intelix.json new file mode 100644 index 0000000..18dd7c1 --- /dev/null +++ b/doc/expansion/sophoslabs_intelix.json @@ -0,0 +1,9 @@ +{ + "description": "An expansion module to query the Sophoslabs intelix API to get additional information about an ip address, url, domain or sha256 attribute.", + "logo": "logos/sophoslabs_intelix.svg", + "requirements": ["A client_id and client_secret pair to authenticate to the SophosLabs Intelix API"], + "input": "An ip address, url, domain or sha256 attribute.", + "output": "SophosLabs Intelix report and lookup objects", + "references": ["https://aws.amazon.com/marketplace/pp/B07SLZPMCS"], + "features": "The module takes an ip address, url, domain or sha256 attribute and queries the SophosLabs Intelix API with the attribute value. The result of this query is a SophosLabs Intelix hash report, or an ip or url lookup, that is then parsed and returned in a MISP object." +} diff --git a/doc/expansion/xlsx-enrich.json b/doc/expansion/xlsx_enrich.json similarity index 100% rename from doc/expansion/xlsx-enrich.json rename to doc/expansion/xlsx_enrich.json diff --git a/doc/logos/google.png b/doc/logos/google.png new file mode 100644 index 0000000000000000000000000000000000000000..492f44c27eb2d402e0dd3446b4d38192e75874c4 GIT binary patch literal 15903 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�ZkvUe1M)ErGJS$u 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 literal 0 HcmV?d00001 diff --git a/doc/logos/intel471.png b/doc/logos/intel471.png new file mode 100644 index 0000000000000000000000000000000000000000..08264e9499fe9b749ccf0a609a488c9c5eb69657 GIT binary patch literal 6713 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 G5B>{C#F}mZ literal 0 HcmV?d00001 diff --git a/doc/logos/sophoslabs_intelix.svg b/doc/logos/sophoslabs_intelix.svg new file mode 100644 index 0000000..9fe952f --- /dev/null +++ b/doc/logos/sophoslabs_intelix.svg @@ -0,0 +1,32 @@ + + + + CC812F0D-F9F0-4D68-9347-3579CDA181A3 + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 48635d8f1b11ff9cd23d8a1b4d2995ce1bf91971 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 2 Oct 2020 17:01:02 +0200 Subject: [PATCH 192/220] add: Added documentation for the socialscan new module - Also quick fix of the message for an invalid result or response concerning the queried email address or username --- README.md | 3 ++- doc/README.md | 16 ++++++++++++++++ doc/expansion/socialscan.json | 8 ++++++++ misp_modules/modules/expansion/socialscan.py | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 doc/expansion/socialscan.json diff --git a/README.md b/README.md index 26dce03..ce9b57c 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,8 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [shodan](misp_modules/modules/expansion/shodan.py) - a minimal [shodan](https://www.shodan.io/) expansion module. * [Sigma queries](misp_modules/modules/expansion/sigma_queries.py) - Experimental expansion module querying a sigma rule to convert it into all the available SIEM signatures. * [Sigma syntax validator](misp_modules/modules/expansion/sigma_syntax_validator.py) - Sigma syntax validator. -* [SophosLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SophosLabs Intelix is an API for Threat Intelligence and Analysis (free tier availible). [SophosLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS) +* [Socialscan](misp_modules/modules/expansion/socialscan.py) - a hover module to check if an email address or a username is used on different online platforms, using the [socialscan](https://github.com/iojw/socialscan) python library +* [SophosLabs Intelix](misp_modules/modules/expansion/sophoslabs_intelix.py) - SophosLabs Intelix is an API for Threat Intelligence and Analysis (free tier available). [SophosLabs](https://aws.amazon.com/marketplace/pp/B07SLZPMCS) * [sourcecache](misp_modules/modules/expansion/sourcecache.py) - a module to cache a specific link from a MISP instance. * [STIX2 pattern syntax validator](misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) - a module to check a STIX2 pattern syntax. * [ThreatCrowd](misp_modules/modules/expansion/threatcrowd.py) - an expansion module for [ThreatCrowd](https://www.threatcrowd.org/). diff --git a/doc/README.md b/doc/README.md index 1225780..1407ae7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1214,6 +1214,22 @@ An expansion hover module to perform a syntax check on sigma rules. ----- +#### [socialscan](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/socialscan.py) + +A hover module to get information on the availability of an email address or username on some online platforms. +- **features**: +>The module takes an email address or username as input and check its availability on some online platforms. The results for each platform are then returned to see if the email address or the username is used, available or if there is an issue with it. +- **input**: +>An email address or usename attribute. +- **output**: +>Text containing information about the availability of an email address or a username in some online platforms. +- **references**: +>https://github.com/iojw/socialscan +- **requirements**: +>The socialscan python library + +----- + #### [sophoslabs_intelix](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sophoslabs_intelix.py) diff --git a/doc/expansion/socialscan.json b/doc/expansion/socialscan.json new file mode 100644 index 0000000..a1cf359 --- /dev/null +++ b/doc/expansion/socialscan.json @@ -0,0 +1,8 @@ +{ + "description": "A hover module to get information on the availability of an email address or username on some online platforms.", + "requirements": ["The socialscan python library"], + "input": "An email address or usename attribute.", + "output": "Text containing information about the availability of an email address or a username in some online platforms.", + "references": ["https://github.com/iojw/socialscan"], + "features": "The module takes an email address or username as input and check its availability on some online platforms. The results for each platform are then returned to see if the email address or the username is used, available or if there is an issue with it." +} diff --git a/misp_modules/modules/expansion/socialscan.py b/misp_modules/modules/expansion/socialscan.py index da30e4c..54f58f6 100644 --- a/misp_modules/modules/expansion/socialscan.py +++ b/misp_modules/modules/expansion/socialscan.py @@ -51,7 +51,7 @@ def parse_results(query_results, feature): results.append(f'Unable to retrieve the {feature} on {result.platform}.') continue if not result.valid: - results.append(f'Invalid response from {result.platform}.') + results.append(f'Invalid response from {result.platform}, or invalid {feature}.') continue statement = 'No account' if result.available else 'There is an account' results.append(f'{statement} linked to the {feature} on {result.platform}.') From a907613ce2350db9ac2739955eeb05f0a2570c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Fri, 9 Oct 2020 14:24:19 +0200 Subject: [PATCH 193/220] chg: Bump deps --- Pipfile.lock | 855 +++++++++++++++++++++++++++------------------------ 1 file changed, 456 insertions(+), 399 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 73aeaed..ef0923f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -57,11 +57,10 @@ }, "assemblyline-client": { "hashes": [ - "sha256:6f45cab3be3ec60984a5c2049d46dac80d4e3d4f3d9538220518a44c7a6ddb15", - "sha256:971371065f2b41027325bf9fa9c72960262a446c7e08bda57865d34dcc4108b0" + "sha256:6a36a654185ba40d10bdd0213a1926aacb4351290824e406cbff6b6b5b251f5f" ], "index": "pypi", - "version": "==3.7.3" + "version": "==4.0.1" }, "async-timeout": { "hashes": [ @@ -73,11 +72,11 @@ }, "attrs": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", + "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==20.2.0" }, "backscatter": { "hashes": [ @@ -89,12 +88,12 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7", - "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8", - "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c" + "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", + "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", + "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666" ], "index": "pypi", - "version": "==4.9.1" + "version": "==4.9.3" }, "blockchain": { "hashes": [ @@ -112,36 +111,44 @@ }, "cffi": { "hashes": [ - "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff", - "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b", - "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac", - "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0", - "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384", - "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26", - "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6", - "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b", - "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e", - "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd", - "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2", - "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66", - "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc", - "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8", - "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55", - "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4", - "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5", - "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d", - "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78", - "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa", - "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793", - "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f", - "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a", - "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f", - "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30", - "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f", - "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3", - "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c" + "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", + "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", + "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", + "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", + "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", + "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", + "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", + "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", + "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", + "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", + "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", + "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", + "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", + "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", + "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", + "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", + "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", + "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", + "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", + "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", + "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", + "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", + "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", + "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", + "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", + "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", + "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", + "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", + "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", + "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", + "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", + "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", + "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", + "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", + "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", + "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" ], - "version": "==1.14.0" + "version": "==1.14.3" }, "chardet": { "hashes": [ @@ -175,35 +182,38 @@ }, "configparser": { "hashes": [ - "sha256:2ca44140ee259b5e3d8aaf47c79c36a7ab0d5e94d70bd4105c03ede7a20ea5a1", - "sha256:cffc044844040c7ce04e9acd1838b5f2e5fa3170182f6fda4d2ea8b0099dbadd" + "sha256:005c3b102c96f4be9b8f40dafbd4997db003d07d1caa19f37808be8031475f2a", + "sha256:08e8a59ef1817ac4ed810bb8e17d049566dd6e024e7566f6285c756db2bb4ff8" ], "markers": "python_version >= '3.6'", - "version": "==5.0.0" + "version": "==5.0.1" }, "cryptography": { "hashes": [ - "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", - "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b", - "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5", - "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf", - "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e", - "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b", - "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae", - "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b", - "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0", - "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b", - "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d", - "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229", - "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3", - "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365", - "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55", - "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270", - "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e", - "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785", - "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0" + "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499", + "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154", + "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6", + "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49", + "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f", + "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396", + "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719", + "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db", + "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70", + "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536", + "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe", + "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba", + "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d", + "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7", + "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490", + "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8", + "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921", + "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118", + "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba", + "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3", + "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc", + "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2" ], - "version": "==2.9.2" + "version": "==3.1.1" }, "decorator": { "hashes": [ @@ -222,11 +232,11 @@ }, "dnspython": { "hashes": [ - "sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01", - "sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d" + "sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7", + "sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d" ], "index": "pypi", - "version": "==1.16.0" + "version": "==2.0.0" }, "domaintools-api": { "hashes": [ @@ -273,11 +283,11 @@ }, "geoip2": { "hashes": [ - "sha256:5869e987bc54c0d707264fec4710661332cc38d2dca5a7f9bb5362d0308e2ce0", - "sha256:99ec12d2f1271a73a0a4a2b663fe6ce25fd02289c0a6bef05c0a1c3b30ee95a4" + "sha256:57d8d15de2527e0697bbef44fc16812bba709f03a07ef99297bd56c1df3b1efd", + "sha256:707025542ef076bd8fd80e97138bebdb7812527b2a007d141a27ad98b0370fff" ], "index": "pypi", - "version": "==3.0.0" + "version": "==4.1.0" }, "httplib2": { "hashes": [ @@ -310,10 +320,16 @@ }, "jbxapi": { "hashes": [ - "sha256:58eb7d77a52169309e2322ce874c0f00a7900a515d1d0798ff85973cdb2766e3" + "sha256:8458f01a9b4e4245d61f6fa75edef17e2992192975f746c51ed5392ba9aa7ce5" ], "index": "pypi", - "version": "==3.8.0" + "version": "==3.11.0" + }, + "json-log-formatter": { + "hashes": [ + "sha256:ee187c9a80936cbf1259f73573973450fc24b84a4fb54e53eb0dcff86ea1e759" + ], + "version": "==0.3.0" }, "jsonschema": { "hashes": [ @@ -343,36 +359,40 @@ }, "lxml": { "hashes": [ - "sha256:06748c7192eab0f48e3d35a7adae609a329c6257495d5e53878003660dc0fec6", - "sha256:0790ddca3f825dd914978c94c2545dbea5f56f008b050e835403714babe62a5f", - "sha256:1aa7a6197c1cdd65d974f3e4953764eee3d9c7b67e3966616b41fab7f8f516b7", - "sha256:22c6d34fdb0e65d5f782a4d1a1edb52e0a8365858dafb1c08cb1d16546cf0786", - "sha256:2754d4406438c83144f9ffd3628bbe2dcc6d62b20dbc5c1ec4bc4385e5d44b42", - "sha256:27ee0faf8077c7c1a589573b1450743011117f1aa1a91d5ae776bbc5ca6070f2", - "sha256:2b02c106709466a93ed424454ce4c970791c486d5fcdf52b0d822a7e29789626", - "sha256:2d1ddce96cf15f1254a68dba6935e6e0f1fe39247de631c115e84dd404a6f031", - "sha256:4f282737d187ae723b2633856085c31ae5d4d432968b7f3f478a48a54835f5c4", - "sha256:51bb4edeb36d24ec97eb3e6a6007be128b720114f9a875d6b370317d62ac80b9", - "sha256:7eee37c1b9815e6505847aa5e68f192e8a1b730c5c7ead39ff317fde9ce29448", - "sha256:7fd88cb91a470b383aafad554c3fe1ccf6dfb2456ff0e84b95335d582a799804", - "sha256:9144ce36ca0824b29ebc2e02ca186e54040ebb224292072250467190fb613b96", - "sha256:925baf6ff1ef2c45169f548cc85204433e061360bfa7d01e1be7ae38bef73194", - "sha256:a636346c6c0e1092ffc202d97ec1843a75937d8c98aaf6771348ad6422e44bb0", - "sha256:a87dbee7ad9dce3aaefada2081843caf08a44a8f52e03e0a4cc5819f8398f2f4", - "sha256:a9e3b8011388e7e373565daa5e92f6c9cb844790dc18e43073212bb3e76f7007", - "sha256:afb53edf1046599991fb4a7d03e601ab5f5422a5435c47ee6ba91ec3b61416a6", - "sha256:b26719890c79a1dae7d53acac5f089d66fd8cc68a81f4e4bd355e45470dc25e1", - "sha256:b7462cdab6fffcda853338e1741ce99706cdf880d921b5a769202ea7b94e8528", - "sha256:b77975465234ff49fdad871c08aa747aae06f5e5be62866595057c43f8d2f62c", - "sha256:c47a8a5d00060122ca5908909478abce7bbf62d812e3fc35c6c802df8fb01fe7", - "sha256:c79e5debbe092e3c93ca4aee44c9a7631bdd407b2871cb541b979fd350bbbc29", - "sha256:d8d40e0121ca1606aa9e78c28a3a7d88a05c06b3ca61630242cded87d8ce55fa", - "sha256:ee2be8b8f72a2772e72ab926a3bccebf47bb727bda41ae070dc91d1fb759b726", - "sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9", - "sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529" + "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f", + "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730", + "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f", + "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1", + "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3", + "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7", + "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a", + "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe", + "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1", + "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e", + "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d", + "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20", + "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae", + "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5", + "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba", + "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293", + "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a", + "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6", + "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88", + "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed", + "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843", + "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443", + "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0", + "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304", + "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258", + "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6", + "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1", + "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481", + "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef", + "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd", + "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee" ], "index": "pypi", - "version": "==4.5.1" + "version": "==4.5.2" }, "maclookup": { "hashes": [ @@ -384,10 +404,10 @@ }, "maxminddb": { "hashes": [ - "sha256:f4d28823d9ca23323d113dc7af8db2087aa4f657fafc64ff8f7a8afda871425b" + "sha256:b95d8ed21799e6604683669c7ed3c6a184fcd92434d5762dccdb139b4f29e597" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.5.4" + "markers": "python_version >= '3.6'", + "version": "==2.0.2" }, "misp-modules": { "editable": true, @@ -425,35 +445,35 @@ }, "numpy": { "hashes": [ - "sha256:13af0184177469192d80db9bd02619f6fa8b922f9f327e077d6f2a6acb1ce1c0", - "sha256:26a45798ca2a4e168d00de75d4a524abf5907949231512f372b217ede3429e98", - "sha256:26f509450db547e4dfa3ec739419b31edad646d21fb8d0ed0734188b35ff6b27", - "sha256:30a59fb41bb6b8c465ab50d60a1b298d1cd7b85274e71f38af5a75d6c475d2d2", - "sha256:33c623ef9ca5e19e05991f127c1be5aeb1ab5cdf30cb1c5cf3960752e58b599b", - "sha256:356f96c9fbec59974a592452ab6a036cd6f180822a60b529a975c9467fcd5f23", - "sha256:3c40c827d36c6d1c3cf413694d7dc843d50997ebffbc7c87d888a203ed6403a7", - "sha256:4d054f013a1983551254e2379385e359884e5af105e3efe00418977d02f634a7", - "sha256:63d971bb211ad3ca37b2adecdd5365f40f3b741a455beecba70fd0dde8b2a4cb", - "sha256:658624a11f6e1c252b2cd170d94bf28c8f9410acab9f2fd4369e11e1cd4e1aaf", - "sha256:76766cc80d6128750075378d3bb7812cf146415bd29b588616f72c943c00d598", - "sha256:7b57f26e5e6ee2f14f960db46bd58ffdca25ca06dd997729b1b179fddd35f5a3", - "sha256:7b852817800eb02e109ae4a9cef2beda8dd50d98b76b6cfb7b5c0099d27b52d4", - "sha256:8cde829f14bd38f6da7b2954be0f2837043e8b8d7a9110ec5e318ae6bf706610", - "sha256:a2e3a39f43f0ce95204beb8fe0831199542ccab1e0c6e486a0b4947256215632", - "sha256:a86c962e211f37edd61d6e11bb4df7eddc4a519a38a856e20a6498c319efa6b0", - "sha256:a8705c5073fe3fcc297fb8e0b31aa794e05af6a329e81b7ca4ffecab7f2b95ef", - "sha256:b6aaeadf1e4866ca0fdf7bb4eed25e521ae21a7947c59f78154b24fc7abbe1dd", - "sha256:be62aeff8f2f054eff7725f502f6228298891fd648dc2630e03e44bf63e8cee0", - "sha256:c2edbb783c841e36ca0fa159f0ae97a88ce8137fb3a6cd82eae77349ba4b607b", - "sha256:cbe326f6d364375a8e5a8ccb7e9cd73f4b2f6dc3b2ed205633a0db8243e2a96a", - "sha256:d34fbb98ad0d6b563b95de852a284074514331e6b9da0a9fc894fb1cdae7a79e", - "sha256:d97a86937cf9970453c3b62abb55a6475f173347b4cde7f8dcdb48c8e1b9952d", - "sha256:dd53d7c4a69e766e4900f29db5872f5824a06827d594427cf1a4aa542818b796", - "sha256:df1889701e2dfd8ba4dc9b1a010f0a60950077fb5242bb92c8b5c7f1a6f2668a", - "sha256:fa1fe75b4a9e18b66ae7f0b122543c42debcf800aaafa0212aaff3ad273c2596" + "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5", + "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8", + "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf", + "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c", + "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65", + "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716", + "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a", + "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e", + "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b", + "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45", + "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d", + "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d", + "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02", + "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c", + "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526", + "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15", + "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d", + "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a", + "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1", + "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a", + "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1", + "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9", + "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4", + "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c", + "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd", + "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8" ], "markers": "python_version >= '3.6'", - "version": "==1.19.0" + "version": "==1.19.2" }, "oauth2": { "hashes": [ @@ -470,51 +490,50 @@ }, "opencv-python": { "hashes": [ - "sha256:068928b9907b3d3acd53b129062557d6b0b8b324bfade77f028dbe4dfe482bf2", - "sha256:0e7c91718351449877c2d4141abd64eee1f9c8701bcfaf4e8627bd023e303368", - "sha256:1ab92d807427641ec45d28d5907426aa06b4ffd19c5b794729c74d91cd95090e", - "sha256:31d634dea1b47c231b88d384f90605c598214d0c596443c9bb808e11761829f5", - "sha256:5fdfc0bed37315f27d30ae5ae9bad47ec0a0a28c323739d39c8177b7e0929238", - "sha256:6fa8fac14dd5af4819d475f74af12d65fbbfa391d3110c3a972934a5e6507c24", - "sha256:78cc89ebc808886eb190626ee71ab65e47f374121975f86e4d5f7c0e3ce6bed9", - "sha256:7c7ba11720d01cb572b4b6945d115cb103462c0a28996b44d4e540d06e6a90fd", - "sha256:a37ee82f1b8ed4b4645619c504311e71ce845b78f40055e78d71add5fab7da82", - "sha256:aa3ca1f54054e1c6439fdf1edafa2a2b940a9eaac04a7b422a1cba9b2d7b9690", - "sha256:b9de3dd956574662712da8e285f0f54327959a4e95b96a2847d3c3f5ee7b96e2", - "sha256:c0087b428cef9a32d977390656d91b02245e0e91f909870492df7e39202645dd", - "sha256:d87e506ab205799727f0efa34b3888949bf029a3ada5eb000ff632606370ca6e", - "sha256:d8a55585631f9c9eca4b1a996e9732ae023169cf2f46f69e4518d67d96198226", - "sha256:dcb8da8c5ebaa6360c8555547a4c7beb6cd983dd95ba895bb78b86cc8cf3de2b", - "sha256:e2206bb8c17c0f212f1f356d82d72dd090ff4651994034416da9bf0c29732825", - "sha256:e3c57d6579e5bf85f564d6d48d8ee89868b92879a9232b9975d072c346625e92", - "sha256:ef89cbf332b9a735d8a82e9ff79cc743eeeb775ad1cd7100bc2aa2429b496f07", - "sha256:f45c1c3cdda1857bedd4dfe0bbd49c9419af0cc57f33490341edeae97d18f037", - "sha256:fb3c855347310788e4286b867997be354c55535597966ed5dac876d9166013a4" + "sha256:16864152aa6ac346ef83588d6f4f5dc974d27851c034d6970fcb7b6a98bbd318", + "sha256:23dade76fe0194139112eea7ecdfa02ae09924b1d8d853f17f387a356519e484", + "sha256:27d5b83edd245a12dd6b8562569ad3f23e5ffe30cef8cfcc70756dd24b55d12f", + "sha256:2a2a7590b99d872b193cda0592b2c1cd6561159c31b361597c0e69e8926c8d16", + "sha256:46032d4648c74730115f8522effda8ac39bd0385f07edc7aab57b41cc7617933", + "sha256:4c195597d5286d1cc7259aeaeb7e6c1cde07fec9bddf26523eab1b15709291aa", + "sha256:69c971fefb633cfd334ed195d58e76e87f267649f98a2394f7400b178e918936", + "sha256:80b5b68e9c5dda29205ca112e6d5bd647b6b43cf917cfa5ce178d61675291bba", + "sha256:98676d349fdfc17dba9f23b87e9b6a639733d35f5f0ffcccb90e76c8200568f4", + "sha256:9df617736351100879b70d914366b9f9e38aa227885f2590b48badc4a233119d", + "sha256:b2147317b00b20e8d7e01201221af2b278aed449fa436316c42bc63f653e8245", + "sha256:d838ee4562f52793b1b10876e5067cae1a6bb1c3c575091644be9b88cf45d255", + "sha256:db74a92ef9c2a0810e1436d586b3b15d421a39b72f06263358f15c7a609498e0", + "sha256:e100a4ffdeed8c4afac6a5b3f6b4481efe0ad90e0a0ae2d129478abd4bd790bc", + "sha256:e87d88a820050c0e886c9add48eac2f80ff29207a98cca25869a6868c519daa4", + "sha256:fdf017c5b93d58ad77e2690e59322fd09414705c28d69b52fad4a19985422e6c" ], "index": "pypi", - "version": "==4.2.0.34" + "version": "==4.4.0.44" }, "pandas": { "hashes": [ - "sha256:02f1e8f71cd994ed7fcb9a35b6ddddeb4314822a0e09a9c5b2d278f8cb5d4096", - "sha256:13f75fb18486759da3ff40f5345d9dd20e7d78f2a39c5884d013456cec9876f0", - "sha256:35b670b0abcfed7cad76f2834041dcf7ae47fd9b22b63622d67cdc933d79f453", - "sha256:4c73f373b0800eb3062ffd13d4a7a2a6d522792fa6eb204d67a4fad0a40f03dc", - "sha256:5759edf0b686b6f25a5d4a447ea588983a33afc8a0081a0954184a4a87fd0dd7", - "sha256:5a7cf6044467c1356b2b49ef69e50bf4d231e773c3ca0558807cdba56b76820b", - "sha256:69c5d920a0b2a9838e677f78f4dde506b95ea8e4d30da25859db6469ded84fa8", - "sha256:8778a5cc5a8437a561e3276b85367412e10ae9fff07db1eed986e427d9a674f8", - "sha256:9871ef5ee17f388f1cb35f76dc6106d40cb8165c562d573470672f4cdefa59ef", - "sha256:9c31d52f1a7dd2bb4681d9f62646c7aa554f19e8e9addc17e8b1b20011d7522d", - "sha256:ab8173a8efe5418bbe50e43f321994ac6673afc5c7c4839014cf6401bbdd0705", - "sha256:ae961f1f0e270f1e4e2273f6a539b2ea33248e0e3a11ffb479d757918a5e03a9", - "sha256:b3c4f93fcb6e97d993bf87cdd917883b7dab7d20c627699f360a8fb49e9e0b91", - "sha256:c9410ce8a3dee77653bc0684cfa1535a7f9c291663bd7ad79e39f5ab58f67ab3", - "sha256:f69e0f7b7c09f1f612b1f8f59e2df72faa8a6b41c5a436dde5b615aaf948f107", - "sha256:faa42a78d1350b02a7d2f0dbe3c80791cf785663d6997891549d0f86dc49125e" + "sha256:206d7c3e5356dcadf082e64dc25c24bc8541718045826074f96346e9d6d05a20", + "sha256:24f61f40febe47edac271eda45d683e42838b7db2bd0f82574d9800259d2b182", + "sha256:3a038cd5da602b955d335aa80cbaa0e5774f68501ff47b9c21509906981478da", + "sha256:427be9938b2f79ab298de84f87693914cda238a27cf10580da96caf3dff64115", + "sha256:54f5f564058b0280d588c3758abde82e280702c440db5faf0c686b80336096f9", + "sha256:5a8a84b75ca3a29bb4263b35d5ed9fcaae2b062f014feed8c5daa897339c7d85", + "sha256:84a4ffe668df357e31f98c829536e3a7142c3036c82f996e639f644c5d32eda1", + "sha256:882012763668af54b48f1412bab95c5cc0a7ccce5a2a8221cfc3839a6e3394ef", + "sha256:920d30fdff65a079f071db635d282b4f583c2b26f2b58d5dca218aac7c59974d", + "sha256:a605054fbca71ed1d08bb2aef6f73c84a579bbac956bfe8f9718d5e84cb41248", + "sha256:b11b496c317dbe007898de699fd59eaf687d0fe8c1b7dad109db6010155d28ae", + "sha256:babbeda2f83b0686c9ad38d93b10516e68cdcd5771007eb80a763e98aaf44613", + "sha256:c22e40f1b4d162ca18eb6b2c572e63eef220dbc9cc3de0241cefb77972621bb7", + "sha256:ca31ac8578d48da354cf66a473d4d5ff99277ca71d321dc7ea4e6fad3c6bb0fd", + "sha256:ca71a5aa9eeb3ef5b31feca7d9b6369d6b3d0b2e9c85d7a89abe3ecb013f1e86", + "sha256:d6b1f9d506dc23da2915bcae5c5968990049c9cec44108bd9855d2c7c89d91dc", + "sha256:d89dbc58aec1544722a8d5046f880b597c497ef8a82c5fe695b4b2effafac5ec", + "sha256:df43ea0e9fd9f9672b0de9cac26d01255ad50481994bf3cb4687c21eec2d7bbc", + "sha256:fd6f05b6101d0e76f3e5c26a47be5be7be96ed84ef3981dc1852e76898e73594" ], "index": "pypi", - "version": "==1.0.5" + "version": "==1.1.3" }, "pandas-ods-reader": { "hashes": [ @@ -535,66 +554,68 @@ }, "pdftotext": { "hashes": [ - "sha256:d37864049581fb13cdcf7b23d4ea23dac7ca2e9c646e8ecac1a39275ab1cae03" + "sha256:98aeb8b07a4127e1a30223bd933ef080bbd29aa88f801717ca6c5618380b8aa6" ], "index": "pypi", - "version": "==2.1.4" + "version": "==2.1.5" }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6" ], "index": "pypi", "version": "==7.2.0" }, "progressbar2": { "hashes": [ - "sha256:13f228cf357f94cdef933c91c1e771e52e1b1931dbae48267be8fcdc2ae2ce36", - "sha256:27abf038efe5b1b5dd91ecc5f704bc88683c1e2a0b2c0fee04de80a648634a0c" + "sha256:ef72be284e7f2b61ac0894b44165926f13f5d995b2bf3cd8a8dedc6224b255a7", + "sha256:fe2738e7ecb7df52ad76307fe610c460c52b50f5335fd26c3ab80ff7655ba1e0" ], - "version": "==3.51.4" + "version": "==3.53.1" }, "psutil": { "hashes": [ - "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058", - "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953", - "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4", - "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e", - "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f", - "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38", - "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e", - "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8", - "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26", - "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5", - "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310" + "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8", + "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498", + "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6", + "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c", + "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195", + "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f", + "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb", + "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1", + "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf", + "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2", + "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==5.7.0" + "version": "==5.7.2" }, "pybgpranking": { "editable": true, @@ -612,36 +633,41 @@ }, "pycryptodome": { "hashes": [ - "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", - "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", + "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", - "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", + "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", + "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", + "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", + "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", + "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", + "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", + "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", + "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", + "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd", + "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", - "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", - "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", - "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", - "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", - "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", - "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", - "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", - "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", - "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", - "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", - "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", - "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", - "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", - "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", - "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", + "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", + "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", + "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94", "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", + "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", + "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", + "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", + "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", + "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", + "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e", + "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f", + "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", - "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", - "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", - "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", - "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94" + "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", + "sha256:663f8de2b3df2e744d6e1610506e0ea4e213bde906795953c1e82279c169f0a7", + "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", + "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", + "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" @@ -649,35 +675,40 @@ "pycryptodomex": { "hashes": [ "sha256:06f5a458624c9b0e04c0086c7f84bcc578567dab0ddc816e0476b3057b18339f", - "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", - "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", - "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", - "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", - "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", + "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb", "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", - "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", - "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", - "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", - "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", + "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", - "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", - "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", - "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", - "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", - "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", - "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", - "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", - "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", - "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", - "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", + "sha256:85c108b42e47d4073344ff61d4e019f1d95bb7725ca0fe87d0a2deb237c10e49", + "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", + "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", + "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", - "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060", - "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", + "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", + "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", + "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", + "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", + "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", + "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", - "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb" + "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", + "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", + "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", + "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", + "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", + "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", + "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", + "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", + "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", + "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", + "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", + "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", + "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" @@ -728,7 +759,7 @@ "pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "ec28820cf491ca7d385477996afa0547eb6b6830" + "ref": "bacd4c78cd83d3bf45dcf55cd9ad3514747ac985" }, "pyonyphe": { "editable": true, @@ -767,16 +798,23 @@ }, "pyrsistent": { "hashes": [ - "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" + "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" ], - "version": "==0.16.0" + "markers": "python_version >= '3.5'", + "version": "==0.17.3" }, "pytesseract": { "hashes": [ - "sha256:afd8a5cdf8ab5d35690efbe71cbf5f89419f668ea8dde7649149815d5c5a899a" + "sha256:b79641b7915ff039da22d5591cb2f5ca6cb0ed7c65194c9c750360dc6a1cc87f" ], "index": "pypi", - "version": "==0.3.4" + "version": "==0.3.6" + }, + "python-baseconv": { + "hashes": [ + "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b" + ], + "version": "==1.2.2" }, "python-dateutil": { "hashes": [ @@ -793,6 +831,13 @@ "index": "pypi", "version": "==0.8.10" }, + "python-engineio": { + "hashes": [ + "sha256:36b33c6aa702d9b6a7f527eec6387a2da1a9a24484ec2f086d76576413cef04b", + "sha256:cfded18156862f94544a9f8ef37f56727df731c8552d7023f5afee8369be2db6" + ], + "version": "==3.13.2" + }, "python-magic": { "hashes": [ "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", @@ -807,6 +852,16 @@ "index": "pypi", "version": "==0.6.18" }, + "python-socketio": { + "extras": [ + "client" + ], + "hashes": [ + "sha256:358d8fbbc029c4538ea25bcaa283e47f375be0017fcba829de8a3a731c9df25a", + "sha256:d437f797c44b6efba2f201867cf02b8c96b97dff26d4e4281ac08b45817cd522" + ], + "version": "==4.6.0" + }, "python-utils": { "hashes": [ "sha256:ebaadab29d0cb9dca0a82eab9c405f5be5125dbbff35b8f32cc433fa498dbaa7", @@ -871,49 +926,49 @@ }, "reportlab": { "hashes": [ - "sha256:0f0c2d98e213d51ae527c0301364d3376cb05f6c47251368a9abd4c3197fcefa", - "sha256:1425c7ea60b8691a881ae21ea0f6907a1dc480d84204ccbfea6da41fbee8f594", - "sha256:204f1d245875ab3d076b37c1a18ac8d2e3222842e13cfa282bcd95282be239e5", - "sha256:21627b57249303bf9b5a633099d058ae9f8625fd6f90cfe79348c48fd5a242cd", - "sha256:2e8e3242f80b79f2470f1b5979abbdb41f31b1333543b830749100342f837d40", - "sha256:2eced06dec3f36135c626b9823649ef9cac95c5634d1bc743a15ee470027483b", - "sha256:3472aa0b74a3b2f252dce823f3c3ba6af8a24de0c1729441deaaf50bed6de9f9", - "sha256:3f0353ffefd3afc0061f4794ef608d6c6f32e69816885f4d45c625c20d8eaf5b", - "sha256:4a9f4540a8eddf56d900ceeb8136bd0ca866c208ba3dcbcde73f07405dbadfba", - "sha256:4eea1afb4aa89780734f44175508edff82928fdf460c9bd60bc719dd99041dc3", - "sha256:5803ffebd36de1ada417f50ce65d379ea5a0bf1a2e8f5d5710a031b3b349b726", - "sha256:58f5f72fc8e5932dedcf24789908a81c6b1e13ea4d63bd9a9a39dc698d8c3321", - "sha256:5b588e5f251c76a8d3589023d1c369c7968e0efe2b38ad5948f665edbf6f9e8b", - "sha256:5d922768fe11a58d80694852aba7389d613c15eb1871c5581a2f075996873d57", - "sha256:5d98f297c5cdd5bc0ccf5697c20b03602ee3378c97938d20312662b27cd9a1d6", - "sha256:66d1d96e97a562614943ecb9daf438e392b3d0b033bd5f4a8098ab616dd877da", - "sha256:670650970c7ba7164cf6340bcd182e7e933eff5d65183af98ee77b40cc25a438", - "sha256:67bb95af7bc8ad7925d299f310d15d556d3e7026fe1b60d8e290454604ae0a85", - "sha256:9c999f5d1a600c4970ba293789b6da14e02e3763a8d3d9abe42dcafa8a5318e9", - "sha256:9d62bef5347063a984e63410fa5a69f1d2cc2fdf8d6ed3d0b9d4ea2ccb4b4154", - "sha256:a14a0d603727b6be2e549c52dd42678ab2d06d2721d4580199e3161843e59298", - "sha256:a3a17b46ff1a15eb29370e11796d8914ef4ea67471bdbc4aa9a9eb9284f4e44c", - "sha256:a6d3e20beeba3fd68cec73b8c0785bfa648c06ac76d1f142c60ccb1a8d2506b6", - "sha256:ad7d7003c732f2be42580e3906e92bd9d2aca5e098898c597554be9ca627fad5", - "sha256:af0ee7b50b85543b68b043e61271963ff5671e564e1d620a404c24a24d4f537c", - "sha256:b3eec55274f5ead7e3af2bf0c01b481ffe1b4c6a7dae42b63d85543e9f2f9a0f", - "sha256:b48c21d43a7ab956954591ce3f71db92ce542bb7428db09734425e2b77ac3142", - "sha256:b761905ab85beb79cf7929c9a019f30ad65664e5733d57a30a995e7b9bef06d1", - "sha256:bbae2f054d0f234c3382076efa337802997aca0f3f664e314f65eefb9d694fa9", - "sha256:bd4157d0bc40fb72bb676fc745fdd648022cccaf4ccfbb291af7f48831d0d5d9", - "sha256:bf74cfabf332034f42a54938eb335543cbf92790170300dbe236ba83b7601cd0", - "sha256:c253c8571db2df3886e390a2bfbe917222953054f4643437373b824f64b013cd", - "sha256:ce1277a6acbc62e9966f410f2596ac533ee0cd5df9b69d5fe4406338a169b7d8", - "sha256:ce8f56987e0e456063e311f066a81496b8b9626c846f2cb0ebb554d1a5f40839", - "sha256:d6264a0589ba8032d9c3bdca9a3e87a897ede09b7f6a8ad5e83b57573212e01e", - "sha256:e6fa0c97e3929d00db27e8cf3b2b5771e94f5f179086c4b0e3213dff53637372", - "sha256:f0930f2b6dddd477b3331ec670171a4662336aac1a778e1a30e980a5cbf40b17", - "sha256:f8cb2b4b925ca6b6e4fdefd288a707776ac686c45034f34d4c952f122d11c40b", - "sha256:f9b71539f518323d95850405c49c01fc3d2f0f0b9f3e157de6d2786804fb28a4", - "sha256:fc488e661f99c915362e0373218f8727cecf888eb1b0eb3a8fe1af624a1b9776" + "sha256:0145233d3596fa5828972eb474b5a9f3fd5dea45d6f196fe006a7a7a461fcd03", + "sha256:04fd4a129393006c4ba9cd9fff56b78ad60fe6702326e9260f55d4abac9f1df2", + "sha256:067800caa12ea69e8df0a9206a7eda6697f91a33edb8413b778647d270bc9f34", + "sha256:106a61093cf6084fbcb1272768f090b06137027e09c5e53c573c6c7b90216066", + "sha256:13afbdca2b0844c19ee6804220bb96630f44ffa2571781de66a04e3f83609295", + "sha256:155887770694a1febb4b1bcd2e2856c931225fa1fe8c5ef6772fce47c07f6204", + "sha256:17c906bc410f5eef01795d709ad88663ab98447683d21b6e97bac9b366504a8a", + "sha256:1880282b9a278b4df5139b2083b9116388d9e1fb4a438c60b3cc4ad983da1bc5", + "sha256:2248f9c362f417d108329fdf5083ede1914757534f1b255d6c37a9a6d99c5efe", + "sha256:2dc571be9d2fec76f8bddb540581429eb16057ff9101767d8b15166ad1de70db", + "sha256:35dda0a1994a8fc009bf5826fe34dcdb15e561b05a5a01c506d949accfbdf027", + "sha256:3858534058ab99fbedb34ceae31f85bbadeeb8e4dbb78a58927599a6f0422617", + "sha256:4710d237fe9f729eacbbb7477d14eea00781704e0cdb83c789e610365e40627f", + "sha256:49e32586d3a814a5f77407c0590504a72743ca278518b3c0f90182430f2d87af", + "sha256:4cdb2ab88839f0d36364b71744b742e09699bde9b943aa35da26580831c3f106", + "sha256:5e995f77124933d3e16ddc09f95ab36793083a1cb08ed2557811f8cfb254434b", + "sha256:73bc92579692609837fb13f271f7436fdb7b6ddebb9e10185452d45814c365c3", + "sha256:7931097db5f18e3ac6909a223e94dd3ad0258541f9802effa5b8f519ef9278e4", + "sha256:7eb3d96adb309593bded364d25a32b80f9dc18b2f9a4b2001972194027a77eef", + "sha256:886bdc7c13e6c6513696eb044000491c787fd53a486aa3adea060d34aa3cd028", + "sha256:8c242a2be8d71ff18e11938cf45114d1144544984cd34fea0606f04144d62bea", + "sha256:8f2759d2a81ee992054e7a1123cadd6baff4edecc1249e503bb6decd6b55e8ee", + "sha256:9765c0eec5e6927aaccf6bd460fe24a014d35a3979f2c7507644fd5946775921", + "sha256:9c7173def03fd3048f07bce00d4ca4793efc37239811d9b3eb77edb561363cd2", + "sha256:a1d0e20cae86c6ba5e6626a9e07eca4d298341adfee778f87d5837bc76912135", + "sha256:a5398e7af6136c25a34569132e7e2646c72a2f89e53028ef109fb03b5a2923a6", + "sha256:a690fe672aa51ee3a6ff4c96d2f5d9744d3b6f27c999a795b9c513923f875bfc", + "sha256:b18ea3593d4edc7f05c510ab298d48548d9a4473a643f37661b1669365d7d33c", + "sha256:b727050ec5dfc4baeded07199d4640156f360ff4624b0194d8e91b234fc0c26b", + "sha256:be53e8423f35d3c80b0560aec034226fdab5623bb4d64b962c3f04b65980b3e0", + "sha256:c70e9c9cfdc0596c3912e0d147f42e83c7ac5642ac82d6fe05d85a6326bae14d", + "sha256:ce7c13eb469f864085a546881a3bc9b46e20a73dc1a43b9e84153833e628dee3", + "sha256:d6bd4d59f4b558165f05f9f7dfad37b9d788bcc05c0b37a6b0fcb6165d6893ec", + "sha256:d75114965cc84ee51aaf3d7eda90f3554f3ac67350ebacd1dbb9193a7a525e21", + "sha256:d78fdb967bd7652515d9a23ff3088e32e32ef96332737696e9eb0fda5602bf81", + "sha256:d930a3de0fa9711b9c960dee92ff2b30c3f69568f00f0244834fe28d5563ea9b", + "sha256:e32af1e47076a3fc77e6be5f7e2c8cbbc82fe493a5cd3f6190c0f8980c401e59", + "sha256:e50de7d196f2d3940f3fdea0f30bf67929686d57285b3779fb071d05a810d65f", + "sha256:e7b7e4a0ce0f455a4777528a8a316e87cc6cf887eaa2a4e6a0cc103f031c57c2", + "sha256:e8dd01462a1bb41b6806aa93a703100d3fbba760f8feca96fcec710db9384a25" ], "index": "pypi", - "version": "==3.5.44" + "version": "==3.5.53" }, "requests": { "extras": [ @@ -935,18 +990,18 @@ }, "shodan": { "hashes": [ - "sha256:31b0740ffaf7c5196a26a0b1edf7d271dffe54ea52bb1b34ba87aa231b5c339b" + "sha256:d2d37d47dd084747df672e6d981f6d72d5d03f4ee12f0ce2170e618147578349" ], "index": "pypi", - "version": "==1.23.0" + "version": "==1.23.1" }, "sigmatools": { "hashes": [ - "sha256:5453717e452aa7860c5e6ac80bcee4f398d70956fc2ee9859bc7255067da8736", - "sha256:cdfeb8200c09c0a40ea1a015e57f3b8e2ba62a28352ca05fa015674f640871e3" + "sha256:5cca698e11f9f3f2f80b92cb4873f9958898ad23d26ce78ee4a573777f4f2035", + "sha256:719c6c19ff60177f3a155d0dd2b054a4ad7e906dec3e88dae668c2b2d200f82c" ], "index": "pypi", - "version": "==0.17.0" + "version": "==0.18.1" }, "six": { "hashes": [ @@ -958,16 +1013,16 @@ }, "socketio-client": { "hashes": [ - "sha256:540d8ab209154d1d9cdb97c170c589a14f7d7f17e19c14e2f59f0307e6175485" + "sha256:ef2e362a85ef2816fb224d727319c4b743d63b4dd9e1da99c622c9643fc4e2a0" ], - "version": "==0.5.6" + "version": "==0.5.7.4" }, "soupsieve": { "hashes": [ "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55", "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232" ], - "markers": "python_version >= '3.5'", + "markers": "python_version >= '3.0'", "version": "==2.0.1" }, "sparqlwrapper": { @@ -983,11 +1038,11 @@ }, "stix2-patterns": { "hashes": [ - "sha256:587a82545680311431e5610036dd6c8c247347a24243fafdafaae2df4d6d7799", - "sha256:7fcb2fa67efeac2a8c493d367c93d0ce6243a10e2eff715ae9f2983e6b32b95d" + "sha256:373a3de163e1b146499c6e5a7908e1f0987173139480897728fcbbba6a806f95", + "sha256:5a38f634adc856b7d03e13dd140d38e184ac1ef11077c1ffca28a262fa6d8c41" ], "index": "pypi", - "version": "==1.3.0" + "version": "==1.3.1" }, "tabulate": { "hashes": [ @@ -1013,10 +1068,10 @@ }, "trustar": { "hashes": [ - "sha256:73336b94012427b66ee61db65fc3c2cea2ed743beaa56cdd5a4c1674ef1a7660" + "sha256:47c45674a4a310dc8d932035e0de112de55c1e899663865b996a6b6b2d79cbde" ], "index": "pypi", - "version": "==0.3.29" + "version": "==0.3.33" }, "tzlocal": { "hashes": [ @@ -1048,11 +1103,11 @@ }, "urllib3": { "hashes": [ - "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", - "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.9" + "version": "==1.25.10" }, "uwhois": { "editable": true, @@ -1076,20 +1131,20 @@ }, "vulners": { "hashes": [ - "sha256:00ff8744d07f398880afc1efcab6dac4abb614c84553fa31b2d439f986b8e0db", - "sha256:90a855915b4fb4dbd0325643d9e643602975fcb931162e5dc2e7778d1daa2fd8", - "sha256:f230bfcd42663326b7c9b8fa117752e26cad4ccca528caaab531c5b592af8cb5" + "sha256:4e78fc7492d33a1e612e7d5046e51f4c272eb7febdfc0fc06061648d2153e75a", + "sha256:6b088b7c8da9bdcc16e8283afd4a8f804388f1432d12d17b29b770778113ec62", + "sha256:7964884c1f262004a950d5915d49520d22afa3ab175d473492e2dbcc6b5e0a9a" ], "index": "pypi", - "version": "==1.5.5" + "version": "==1.5.8" }, "wand": { "hashes": [ - "sha256:d5b75ac13d7485032970926415648586eafeeb1eb62ed6ebd0778358cf9d70e0", - "sha256:df0780b1b54938a43d29279a6588fde11e349550c8958a673d57c26a3e6de7f1" + "sha256:566b3d049858efa879096a7ab2e0516d67a240e6c3ffd7a267298c41e81c14b7", + "sha256:d21429288fe0de63d829dbbfb26736ebaed9fd0792c2a0dc5943c5cab803a708" ], "index": "pypi", - "version": "==0.6.1" + "version": "==0.6.3" }, "websocket-client": { "hashes": [ @@ -1114,10 +1169,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:828b3285fc95105f5b1946a6a015b31cf388bd5378fdc6604e4d1b7839df2e77", - "sha256:82a3b0e73e3913483da23791d1a25e4d2dbb3837d1be4129473526b9a270a5cc" + "sha256:99b665203d737db31378ec729c9990a004c1abae53a6fa211c104f8c2e36cffd", + "sha256:b89002dea57bb3d4c8207f3e28ef8244bfd9e936b85d74e7dd1f97e11bb70313" ], - "version": "==1.2.9" + "version": "==1.3.6" }, "yara-python": { "hashes": [ @@ -1138,36 +1193,36 @@ }, "yarl": { "hashes": [ - "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce", - "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6", - "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce", - "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae", - "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d", - "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f", - "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b", - "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b", - "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb", - "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462", - "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea", - "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70", - "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1", - "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a", - "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b", - "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080", - "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2" + "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e", + "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5", + "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580", + "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc", + "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b", + "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2", + "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a", + "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921", + "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e", + "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1", + "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d", + "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131", + "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a", + "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1", + "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188", + "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020", + "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a" ], "markers": "python_version >= '3.5'", - "version": "==1.4.2" + "version": "==1.6.0" } }, "develop": { "attrs": { "hashes": [ - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", + "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==19.3.0" + "version": "==20.2.0" }, "certifi": { "hashes": [ @@ -1185,57 +1240,60 @@ }, "codecov": { "hashes": [ - "sha256:491938ad774ea94a963d5d16354c7299e90422a33a353ba0d38d0943ed1d5091", - "sha256:b67bb8029e8340a7bf22c71cbece5bd18c96261fdebc2f105ee4d5a005bc8728", - "sha256:d8b8109f44edad03b24f5f189dac8de9b1e3dc3c791fa37eeaf8c7381503ec34" + "sha256:24545847177a893716b3455ac5bfbafe0465f38d4eb86ea922c09adc7f327e65", + "sha256:355fc7e0c0b8a133045f0d6089bde351c845e7b52b99fec5903b4ea3ab5f6aab", + "sha256:7877f68effde3c2baadcff807a5d13f01019a337f9596eece0d64e57393adf3a" ], "index": "pypi", - "version": "==2.1.7" + "version": "==2.1.9" }, "coverage": { "hashes": [ - "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a", - "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355", - "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65", - "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7", - "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9", - "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1", - "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0", - "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55", - "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c", - "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6", - "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef", - "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019", - "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e", - "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0", - "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf", - "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24", - "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2", - "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c", - "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4", - "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0", - "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd", - "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04", - "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e", - "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730", - "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2", - "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768", - "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796", - "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7", - "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a", - "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489", - "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052" + "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516", + "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259", + "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9", + "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097", + "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0", + "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f", + "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7", + "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c", + "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5", + "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7", + "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729", + "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978", + "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9", + "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f", + "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9", + "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822", + "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418", + "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82", + "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f", + "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d", + "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221", + "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4", + "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21", + "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709", + "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54", + "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d", + "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270", + "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24", + "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751", + "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a", + "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237", + "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7", + "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636", + "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==5.1" + "version": "==5.3" }, "flake8": { "hashes": [ - "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c", - "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208" + "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", + "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" ], "index": "pypi", - "version": "==3.8.3" + "version": "==3.8.4" }, "idna": { "hashes": [ @@ -1245,6 +1303,13 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.10" }, + "iniconfig": { + "hashes": [ + "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437", + "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69" + ], + "version": "==1.0.1" + }, "mccabe": { "hashes": [ "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -1252,14 +1317,6 @@ ], "version": "==0.6.1" }, - "more-itertools": { - "hashes": [ - "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5", - "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2" - ], - "markers": "python_version >= '3.5'", - "version": "==8.4.0" - }, "nose": { "hashes": [ "sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac", @@ -1319,11 +1376,11 @@ }, "pytest": { "hashes": [ - "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1", - "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8" + "sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9", + "sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92" ], "index": "pypi", - "version": "==5.4.3" + "version": "==6.1.1" }, "requests": { "extras": [ @@ -1344,20 +1401,20 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.15.0" }, + "toml": { + "hashes": [ + "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", + "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + ], + "version": "==0.10.1" + }, "urllib3": { "hashes": [ - "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", - "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.9" - }, - "wcwidth": { - "hashes": [ - "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", - "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" - ], - "version": "==0.2.5" + "version": "==1.25.10" } } } From 095fbfd75f6d34c89cb3804b8255b7adb5a3402c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Fri, 9 Oct 2020 14:40:46 +0200 Subject: [PATCH 194/220] chg: Bump deps --- Pipfile.lock | 164 +++++++++++++++++++++++++-------------------------- REQUIREMENTS | 131 ++++++++++++++++++++-------------------- 2 files changed, 149 insertions(+), 146 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index ef0923f..1229e98 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -561,34 +561,34 @@ }, "pillow": { "hashes": [ - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", + "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6" + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" ], "index": "pypi", "version": "==7.2.0" @@ -633,41 +633,41 @@ }, "pycryptodome": { "hashes": [ - "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", - "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", - "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", - "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", - "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", - "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", - "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", - "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", - "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", - "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", - "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd", "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", + "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", + "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", + "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", - "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", - "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", - "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", - "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94", - "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", - "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", - "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", - "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", - "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", - "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", + "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", + "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", + "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", - "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e", - "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f", "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", - "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", - "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", + "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", "sha256:663f8de2b3df2e744d6e1610506e0ea4e213bde906795953c1e82279c169f0a7", - "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", + "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", + "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", - "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21" + "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", + "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", + "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd", + "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", + "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", + "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", + "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc", + "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a", + "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", + "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e", + "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", + "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", + "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a", + "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", + "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f", + "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", + "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", + "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" @@ -675,40 +675,40 @@ "pycryptodomex": { "hashes": [ "sha256:06f5a458624c9b0e04c0086c7f84bcc578567dab0ddc816e0476b3057b18339f", - "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb", - "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", - "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", + "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", + "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", + "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", + "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", + "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", + "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", + "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", + "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", + "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", + "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", + "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", "sha256:85c108b42e47d4073344ff61d4e019f1d95bb7725ca0fe87d0a2deb237c10e49", - "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", - "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", - "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", - "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", - "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", - "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", - "sha256:17272d06e4b2f6455ee2cbe93e8eb50d9450a5dc6223d06862ee1ea5d1235861", - "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", - "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", - "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", - "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", - "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", - "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", - "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", - "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", - "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", - "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", - "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", - "sha256:48cc2cfc251f04a6142badeb666d1ff49ca6fdfc303fd72579f62b768aaa52b9", - "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", - "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", - "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", - "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", - "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", - "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", - "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060" + "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", + "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", + "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", + "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", + "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", + "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", + "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", + "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", + "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", + "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", + "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", + "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", + "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060", + "sha256:e42860fbe1292668b682f6dabd225fbe2a7a4fa1632f0c39881c019e93dea594", + "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", + "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", + "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", + "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==3.9.8" diff --git a/REQUIREMENTS b/REQUIREMENTS index 73b002a..955ecad 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -3,110 +3,113 @@ -e git+https://github.com/D4-project/BGP-Ranking.git/@fd9c0e03af9b61d4bf0b67ac73c7208a55178a54#egg=pybgpranking&subdirectory=client -e git+https://github.com/D4-project/IPASN-History.git/@fc5e48608afc113e101ca6421bf693b7b9753f9e#egg=pyipasnhistory&subdirectory=client -e git+https://github.com/MISP/PyIntel471.git@0df8d51f1c1425de66714b3a5a45edb69b8cc2fc#egg=pyintel471 --e git+https://github.com/MISP/PyMISP.git@b5b40ae2c5225a4b349c26294cfc012309a61352#egg=pymisp[fileobjects,openioc,virustotal,pdfexport] --e git+https://github.com/Rafiot/uwhoisd.git@411572840eba4c72dc321c549b36a54ed5cea9de#egg=uwhois&subdirectory=client +-e git+https://github.com/MISP/PyMISP.git@bacd4c78cd83d3bf45dcf55cd9ad3514747ac985#egg=pymisp[fileobjects,openioc,pdfexport] +-e git+https://github.com/Rafiot/uwhoisd.git@783bba09b5a6964f25566089826a1be4b13f2a22#egg=uwhois&subdirectory=client -e git+https://github.com/cartertemm/ODTReader.git/@49d6938693f6faa3ff09998f86dba551ae3a996b#egg=odtreader -e git+https://github.com/sebdraven/pydnstrails@48c1f740025c51289f43a24863d1845ff12fd21a#egg=pydnstrails -e git+https://github.com/sebdraven/pyonyphe@1ce15581beebb13e841193a08a2eb6f967855fcb#egg=pyonyphe --e git+https://github.com/stricaud/faup.git#egg=pyfaup&subdirectory=src/lib/bindings/python -aiohttp==3.4.4 -antlr4-python3-runtime==4.8 ; python_version >= '3' +aiohttp==3.6.2; python_full_version >= '3.5.3' +antlr4-python3-runtime==4.8; python_version >= '3' apiosintds==1.8.3 argparse==1.4.0 -assemblyline-client==3.7.3 -async-timeout==3.0.1 -attrs==19.3.0 +assemblyline-client==4.0.1 +async-timeout==3.0.1; python_full_version >= '3.5.3' +attrs==20.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' backscatter==0.2.4 -beautifulsoup4==4.8.2 +beautifulsoup4==4.9.3 blockchain==1.4.4 -censys==0.0.8 -certifi==2019.11.28 -cffi==1.14.0 +certifi==2020.6.20 +cffi==1.14.3 chardet==3.0.4 click-plugins==1.1.1 -click==7.1.1 -colorama==0.4.3 -cryptography==2.8 +click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +colorama==0.4.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +configparser==5.0.1; python_version >= '3.6' +cryptography==3.1.1 decorator==4.4.2 -deprecated==1.2.7 -dnspython==1.16.0 -domaintools-api==0.3.3 +deprecated==1.2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +dnspython==2.0.0 +domaintools-api==0.5.2 enum-compat==0.0.3 ez-setup==0.9 ezodf==0.3.2 -future==0.18.2 +future==0.18.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' futures==3.1.1 -geoip2==3.0.0 -httplib2==0.17.0 -idna-ssl==1.1.0 ; python_version < '3.7' -idna==2.9 -importlib-metadata==1.6.0 ; python_version < '3.8' +geoip2==4.1.0 +httplib2==0.18.1 +idna-ssl==1.1.0; python_version < '3.7' +idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' isodate==0.6.0 -jbxapi==3.4.0 +jbxapi==3.11.0 +json-log-formatter==0.3.0 jsonschema==3.2.0 lief==0.10.1 -lxml==4.5.0 +lxml==4.5.2 maclookup==1.0.3 -maxminddb==1.5.2 -multidict==4.7.5 +maxminddb==2.0.2; python_version >= '3.6' +multidict==4.7.6; python_version >= '3.5' np==1.0.2 -numpy==1.18.2 +numpy==1.19.2; python_version >= '3.6' oauth2==1.9.0.post1 -opencv-python==4.2.0.32 +opencv-python==4.4.0.44 pandas-ods-reader==0.0.7 -pandas==1.0.3 +pandas==1.1.3 passivetotal==1.0.31 -pdftotext==2.1.4 -pillow==7.0.0 -progressbar2==3.50.1 -psutil==5.7.0 -pycparser==2.20 -pycryptodome==3.9.7 -pycryptodomex==3.9.7 +pdftotext==2.1.5 +pillow==7.2.0 +progressbar2==3.53.1 +psutil==5.7.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pycryptodome==3.9.8; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +pycryptodomex==3.9.8; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' pydeep==0.4 -pyeupi==1.0 +pyeupi==1.1 pygeoip==0.3.2 pyopenssl==19.1.0 -pyparsing==2.4.6 +pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' pypdns==1.5.1 pypssl==2.1 -pyrsistent==0.16.0 -pytesseract==0.3.3 -python-dateutil==2.8.1 +pyrsistent==0.17.3; python_version >= '3.5' +pytesseract==0.3.6 +python-baseconv==1.2.2 +python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-docx==0.8.10 -python-magic==0.4.15 +python-engineio==3.13.2 +python-magic==0.4.18 python-pptx==0.6.18 +python-socketio[client]==4.6.0 python-utils==2.4.0 pytz==2019.3 pyyaml==5.3.1 pyzbar==0.1.8 -pyzipper==0.3.1 ; python_version >= '3.5' -rdflib==4.2.2 -redis==3.4.1 -reportlab==3.5.42 +pyzipper==0.3.3; python_version >= '3.5' +rdflib==5.0.0 +redis==3.5.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +reportlab==3.5.53 requests-cache==0.5.2 -requests[security]==2.23.0 -shodan==1.22.0 -sigmatools==0.16.0 -six==1.14.0 -socketio-client==0.5.6 -soupsieve==2.0 +requests[security]==2.24.0 +shodan==1.23.1 +sigmatools==0.18.1 +six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +socketio-client==0.5.7.4 +soupsieve==2.0.1; python_version >= '3.0' sparqlwrapper==1.8.5 -stix2-patterns==1.3.0 +stix2-patterns==1.3.1 tabulate==0.8.7 -tornado==6.0.4 -trustar==0.3.28 -url-normalize==1.4.1 +tornado==6.0.4; python_version >= '3.5' +trustar==0.3.33 +tzlocal==2.1 +unicodecsv==0.14.1 +url-normalize==1.4.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' urlarchiver==0.2 -urllib3==1.25.8 +urllib3==1.25.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' validators==0.14.0 vt-graph-api==1.0.1 -vulners==1.5.5 -wand==0.5.9 +vulners==1.5.8 +wand==0.6.3 websocket-client==0.57.0 wrapt==1.12.1 xlrd==1.2.0 -xlsxwriter==1.2.8 +xlsxwriter==1.3.6 yara-python==3.8.1 -yarl==1.4.2 -zipp==3.1.0 +yarl==1.6.0; python_version >= '3.5' From f2de7ab87f067b32a3332b789470c3d41ad0e3a3 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 17 Oct 2020 20:41:02 +0200 Subject: [PATCH 195/220] new: [clamav] Module for malware scan by ClamAV --- REQUIREMENTS | 1 + misp_modules/modules/expansion/clamav.py | 127 +++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 misp_modules/modules/expansion/clamav.py diff --git a/REQUIREMENTS b/REQUIREMENTS index 955ecad..9b26d1c 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -26,6 +26,7 @@ click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, colorama==0.4.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' configparser==5.0.1; python_version >= '3.6' cryptography==3.1.1 +clamd==1.0.2 decorator==4.4.2 deprecated==1.2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' dnspython==2.0.0 diff --git a/misp_modules/modules/expansion/clamav.py b/misp_modules/modules/expansion/clamav.py new file mode 100644 index 0000000..2358691 --- /dev/null +++ b/misp_modules/modules/expansion/clamav.py @@ -0,0 +1,127 @@ +import base64 +import io +import json +import logging +import sys +import zipfile +import clamd +from typing import Optional +from pymisp import MISPEvent, MISPObject + +log = logging.getLogger("clamav") +log.setLevel(logging.DEBUG) +sh = logging.StreamHandler(sys.stdout) +sh.setLevel(logging.DEBUG) +fmt = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +sh.setFormatter(fmt) +log.addHandler(sh) + +moduleinfo = { + "full_name": "ClamAV", + "version": "0.1", + "author": "Jakub Onderka", + "description": "Submit file to ClamAV", + "module-type": ["expansion"] +} +moduleconfig = ["connection"] +mispattributes = { + "input": ["attachment", "malware-sample"], + "format": "misp_standard" +} + + +def create_response(software: str, signature: Optional[str] = None) -> dict: + misp_event = MISPEvent() + if signature: + av_signature_object = MISPObject("av-signature") + av_signature_object.add_attribute("signature", signature) + av_signature_object.add_attribute("software", software) + + misp_event.add_object(av_signature_object) + + event = json.loads(misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {"results": results} + + +def connect_to_clamav(connection_string: str) -> clamd.ClamdNetworkSocket: + if connection_string.startswith("unix://"): + return clamd.ClamdUnixSocket(connection_string.replace("unix://", "")) + elif ":" in connection_string: + host, port = connection_string.split(":") + return clamd.ClamdNetworkSocket(host, port) + else: + raise Exception("ClamAV connection string is invalid") + + +def handler(q=False): + if q is False: + return False + + request = json.loads(q) + + connection_string: str = request["config"].get("connection") + if not connection_string: + return {"error": "No ClamAV connection string provided"} + + attribute = request.get("attribute") + if not attribute: + return {"error": "No attribute provided"} + + attribute_type = attribute.get("type") + if not attribute_type: + return {"error": "No attribute type provided"} + + if attribute_type not in mispattributes["input"]: + return {"error": "Invalid attribute type provided, expected 'malware-sample' or 'attachment'"} + + attribute_data = attribute.get("data") + if not attribute_data: + return {"error": "No attribute data provided"} + + try: + clamav = connect_to_clamav(connection_string) + software_version = clamav.version() + except Exception: + logging.exception("Could not connect to ClamAV") + return {"error": "Could not connect to ClamAV"} + + try: + data = base64.b64decode(attribute_data, validate=True) + except Exception: + logging.exception("Provided data is not valid base64 encoded string") + return {"error": "Provided data is not valid base64 encoded string"} + + if attribute_type == "malware-sample": + try: + with zipfile.ZipFile(io.BytesIO(data)) as zipf: + data = zipf.read(zipf.namelist()[0], pwd=b"infected") + except Exception: + logging.exception("Could not extract malware sample from ZIP file") + return {"error": "Could not extract malware sample from ZIP file"} + + try: + status, reason = clamav.instream(io.BytesIO(data))["stream"] + except Exception: + logging.exception("Could not send attribute data to ClamAV. Maybe file is too big?") + return {"error": "Could not send attribute data to ClamAV. Maybe file is too big?"} + + if status == "ERROR": + return {"error": "ClamAV returned error message: {}".format(reason)} + elif status == "OK": + return {"results": {}} + elif status == "FOUND": + return create_response(software_version, reason) + else: + return {"error": "ClamAV returned invalid status {}: {}".format(status, reason)} + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo From 0872bb820c75a522c3947c372b0ebd63b9086c54 Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Tue, 20 Oct 2020 10:17:52 +0200 Subject: [PATCH 196/220] chg: [clamav] TCP port connection must be an integer --- misp_modules/modules/expansion/clamav.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/clamav.py b/misp_modules/modules/expansion/clamav.py index 2358691..2eb0c62 100644 --- a/misp_modules/modules/expansion/clamav.py +++ b/misp_modules/modules/expansion/clamav.py @@ -51,7 +51,7 @@ def connect_to_clamav(connection_string: str) -> clamd.ClamdNetworkSocket: return clamd.ClamdUnixSocket(connection_string.replace("unix://", "")) elif ":" in connection_string: host, port = connection_string.split(":") - return clamd.ClamdNetworkSocket(host, port) + return clamd.ClamdNetworkSocket(host, int(port)) else: raise Exception("ClamAV connection string is invalid") From 7ad5eb0bfad089069541aa76f4e1d7371db4b55f Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 20 Oct 2020 13:23:14 +0200 Subject: [PATCH 197/220] chg: [clamav] Add reference to original attribute --- misp_modules/modules/expansion/clamav.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/misp_modules/modules/expansion/clamav.py b/misp_modules/modules/expansion/clamav.py index 2eb0c62..1582409 100644 --- a/misp_modules/modules/expansion/clamav.py +++ b/misp_modules/modules/expansion/clamav.py @@ -5,6 +5,7 @@ import logging import sys import zipfile import clamd +from . import check_input_attribute, standard_error_message from typing import Optional from pymisp import MISPEvent, MISPObject @@ -19,7 +20,6 @@ sh.setFormatter(fmt) log.addHandler(sh) moduleinfo = { - "full_name": "ClamAV", "version": "0.1", "author": "Jakub Onderka", "description": "Submit file to ClamAV", @@ -32,13 +32,15 @@ mispattributes = { } -def create_response(software: str, signature: Optional[str] = None) -> dict: +def create_response(original_attribute: dict, software: str, signature: Optional[str] = None) -> dict: misp_event = MISPEvent() if signature: + misp_event.add_attribute(**original_attribute) + av_signature_object = MISPObject("av-signature") av_signature_object.add_attribute("signature", signature) av_signature_object.add_attribute("software", software) - + av_signature_object.add_reference(original_attribute["uuid"], "belongs-to") misp_event.add_object(av_signature_object) event = json.loads(misp_event.to_json()) @@ -53,7 +55,7 @@ def connect_to_clamav(connection_string: str) -> clamd.ClamdNetworkSocket: host, port = connection_string.split(":") return clamd.ClamdNetworkSocket(host, int(port)) else: - raise Exception("ClamAV connection string is invalid") + raise Exception("ClamAV connection string is invalid. It must be unix socket path with 'unix://' prefix or IP:PORT.") def handler(q=False): @@ -70,11 +72,10 @@ def handler(q=False): if not attribute: return {"error": "No attribute provided"} - attribute_type = attribute.get("type") - if not attribute_type: - return {"error": "No attribute type provided"} + if not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} - if attribute_type not in mispattributes["input"]: + if attribute["type"] not in mispattributes["input"]: return {"error": "Invalid attribute type provided, expected 'malware-sample' or 'attachment'"} attribute_data = attribute.get("data") @@ -94,7 +95,7 @@ def handler(q=False): logging.exception("Provided data is not valid base64 encoded string") return {"error": "Provided data is not valid base64 encoded string"} - if attribute_type == "malware-sample": + if attribute["type"] == "malware-sample": try: with zipfile.ZipFile(io.BytesIO(data)) as zipf: data = zipf.read(zipf.namelist()[0], pwd=b"infected") @@ -113,7 +114,7 @@ def handler(q=False): elif status == "OK": return {"results": {}} elif status == "FOUND": - return create_response(software_version, reason) + return create_response(attribute, software_version, reason) else: return {"error": "ClamAV returned invalid status {}: {}".format(status, reason)} From c00349e1984ee7b00b88eb36e382f9a5c98cd94e Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 22 Oct 2020 23:25:20 +0200 Subject: [PATCH 198/220] fix: [cve-advanced] Using the cpe and weakness attribute types --- .../modules/expansion/cve_advanced.py | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/misp_modules/modules/expansion/cve_advanced.py b/misp_modules/modules/expansion/cve_advanced.py index d15711f..9071ff9 100644 --- a/misp_modules/modules/expansion/cve_advanced.py +++ b/misp_modules/modules/expansion/cve_advanced.py @@ -23,9 +23,9 @@ class VulnerabilityParser(): self.references = defaultdict(list) self.capec_features = ('id', 'name', 'summary', 'prerequisites', 'solutions') self.vulnerability_mapping = { - 'id': ('text', 'id'), 'summary': ('text', 'summary'), - 'vulnerable_configuration': ('text', 'vulnerable_configuration'), - 'vulnerable_configuration_cpe_2_2': ('text', 'vulnerable_configuration'), + 'id': ('vulnerability', 'id'), 'summary': ('text', 'summary'), + 'vulnerable_configuration': ('cpe', 'vulnerable_configuration'), + 'vulnerable_configuration_cpe_2_2': ('cpe', 'vulnerable_configuration'), 'Modified': ('datetime', 'modified'), 'Published': ('datetime', 'published'), 'references': ('link', 'references'), 'cvss': ('float', 'cvss-score')} self.weakness_mapping = {'name': 'name', 'description_summary': 'description', @@ -71,33 +71,39 @@ class VulnerabilityParser(): break def __parse_capec(self, vulnerability_uuid): - attribute_type = 'text' for capec in self.vulnerability['capec']: capec_object = MISPObject('attack-pattern') for feature in self.capec_features: - capec_object.add_attribute(feature, **dict(type=attribute_type, value=capec[feature])) + capec_object.add_attribute(feature, **{'type': 'text', 'value': capec[feature]}) for related_weakness in capec['related_weakness']: - attribute = dict(type='weakness', value="CWE-{}".format(related_weakness)) + attribute = {'type': 'weakness', 'value': f"CWE-{related_weakness}"} capec_object.add_attribute('related-weakness', **attribute) self.misp_event.add_object(capec_object) - self.references[vulnerability_uuid].append(dict(referenced_uuid=capec_object.uuid, - relationship_type='targeted-by')) + self.references[vulnerability_uuid].append( + { + 'referenced_uuid': capec_object.uuid, + 'relationship_type': 'targeted-by' + } + ) def __parse_weakness(self, vulnerability_uuid): - attribute_type = 'text' cwe_string, cwe_id = self.vulnerability['cwe'].split('-') cwes = requests.get(self.api_url.replace('/cve/', '/cwe')) if cwes.status_code == 200: for cwe in cwes.json(): if cwe['id'] == cwe_id: weakness_object = MISPObject('weakness') - weakness_object.add_attribute('id', **dict(type=attribute_type, value='-'.join([cwe_string, cwe_id]))) + weakness_object.add_attribute('id', {'type': 'weakness', 'value': f'{cwe_string}-{cwe_id}'}) for feature, relation in self.weakness_mapping.items(): if cwe.get(feature): - weakness_object.add_attribute(relation, **dict(type=attribute_type, value=cwe[feature])) + weakness_object.add_attribute(relation, **{'type': 'text', 'value': cwe[feature]}) self.misp_event.add_object(weakness_object) - self.references[vulnerability_uuid].append(dict(referenced_uuid=weakness_object.uuid, - relationship_type='weakened-by')) + self.references[vulnerability_uuid].append( + { + 'referenced_uuid': weakness_object.uuid, + 'relationship_type': 'weakened-by' + } + ) break From 410aaaeb2896bfb69d14b7ac2f293a22f914fadc Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 23 Oct 2020 21:19:26 +0200 Subject: [PATCH 199/220] add: First shot of an expansio module to query cve-search with a cpe to get the related vulnerabilities --- misp_modules/modules/expansion/cpe.py | 122 ++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 misp_modules/modules/expansion/cpe.py diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py new file mode 100644 index 0000000..74cb1b6 --- /dev/null +++ b/misp_modules/modules/expansion/cpe.py @@ -0,0 +1,122 @@ +import json +import requests +from . import check_input_attribute, standard_error_message + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'} +moduleinfo = { + 'version': '1', + 'author': 'Christian Studer', + 'description': 'An expansion module to enrich a CPE attribute with the related vulnerabilities.', + 'module-type': ['expansion', 'hover'] +} +moduleconfig = ["custom_API_URL"] + + +class VulnerabilityParser(): + def __init__(self, attribute, api_url): + self.attribute = attribute + self.api_url = api_url + self.misp_event = MISPEvent() + self.misp_event.add_attribute(**attribute) + self.vulnerability_mapping = { + 'id': { + 'type': 'vulnerability', + 'object_relation': 'id' + }, + 'summary': { + 'type': 'text', + 'object_relation': 'summary' + }, + 'vulnerable_configuration': { + 'type': 'cpe', + 'object_relation': 'vulnerable_configuration' + }, + 'vulnerable_configuration_cpe_2_2': { + 'type': 'cpe', + 'object_relation': 'vulnerable_configuration' + }, + 'Modified': { + 'type': 'datetime', + 'object_relation': 'modified' + }, + 'Published': { + 'type': 'datetime', + 'object_relation': 'published' + }, + 'references': { + 'type': 'link', + 'object_relation': 'references' + }, + 'cvss': { + 'type': 'float', + 'object_relation': 'cvss-score' + } + } + + def parse_vulnerabilities(self, vulnerabilities): + for vulnerability in vulnerabilities: + vulnerability_object = MISPObject('vulnerability') + for feature in ('id', 'summary', 'Modified', 'Published', 'cvss'): + if vulnerability.get(feature): + attribute = {'value': vulnerability[feature]} + atttribute.update(self.vulnerability_mapping[feature]) + vulnerability_object.add_attribute(**attribute) + if vulnerability.get('Published'): + vulnerability_object.add_attribute(**{ + 'type': 'text', + 'object_relation': 'state', + 'value': 'Published' + }) + for feature in ('references', 'vulnerable_configuration', 'vulnerable_configuration_cpe_2_2'): + if vulnerability.get(feature): + for value in vulnerability[feature]: + if isinstance(value, dict): + value = value['title'] + attribute = {'value': value} + attribute.update(self.vulnerability_mapping[feature]) + vulnerability_object.add_attribute(**attribute) + vulnerability_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(vulnerability_object) + + def get_result(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + + +def check_url(url): + return "{}/".format(url) if not url.endswith('/') else url + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] + if attribute.get('type') != 'cpe': + return {'error': 'Wrong input attribute type.'} + if not request.get('config') or not request['config'].get('custom_API_URL'): + return {'error': 'Missing API URL'} + api_url = check_url(request['config']['custom_API_URL']) + response = requests.get("{}{}".format(api_url, attribute['value'])) + if response.status_code == 200: + vulnerabilities = response.json() + if not vulnerabilities: + return {'error': 'No related vulnerability for this CPE.'} + else: + return {'error': 'API not accessible.'} + parser = VulnerabilitiesParser(attribute, api_url) + parser.parse_vulnerabilities(vulnerabilities) + return parser.get_result() + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 2be1d7a0cde74703e15e2afd70c4205d9a4c3844 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Fri, 23 Oct 2020 22:17:47 +0200 Subject: [PATCH 200/220] new: [expansion] Added html_to_markdown module It fetches the HTML from the provided URL, performs a bit of DOM clean-up then convert it into markdown --- REQUIREMENTS | 1 + misp_modules/modules/expansion/__init__.py | 2 +- .../modules/expansion/html_to_markdown.py | 53 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100755 misp_modules/modules/expansion/html_to_markdown.py diff --git a/REQUIREMENTS b/REQUIREMENTS index 9b26d1c..f6362b5 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -47,6 +47,7 @@ jsonschema==3.2.0 lief==0.10.1 lxml==4.5.2 maclookup==1.0.3 +markdownify==0.5.3 maxminddb==2.0.2; python_version >= '3.6' multidict==4.7.6; python_version >= '3.5' np==1.0.2 diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 1b6d2bb..6485140 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'virustotal_public', 'apiosintds', 'urlscan', 'securitytrails', 'apivoid', 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', - 'trustar_enrich', 'recordedfuture'] + 'trustar_enrich', 'recordedfuture', 'html_to_markdown'] minimum_required_fields = ('type', 'uuid', 'value') diff --git a/misp_modules/modules/expansion/html_to_markdown.py b/misp_modules/modules/expansion/html_to_markdown.py new file mode 100755 index 0000000..228b4bc --- /dev/null +++ b/misp_modules/modules/expansion/html_to_markdown.py @@ -0,0 +1,53 @@ +import json +import requests +from markdownify import markdownify +from bs4 import BeautifulSoup + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['url'], 'output': ['text']} +moduleinfo = {'version': '0.1', 'author': 'Sami Mokaddem', + 'description': 'Simple HTML fetcher', + 'module-type': ['expansion']} + + +def fetchHTML(url): + r = requests.get(url) + return r.text + + +def stripUselessTags(html): + soup = BeautifulSoup(html, 'html.parser') + toRemove = ['script', 'head', 'header', 'footer', 'meta', 'link'] + for tag in soup.find_all(toRemove): + tag.decompose() + return str(soup) + + +def convertHTML(html): + toStrip = ['a', 'img'] + return markdownify(html, heading_style='ATX', strip=toStrip) + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if request.get('url'): + url = request['url'] + else: + return False + html = fetchHTML(url) + html = stripUselessTags(html) + markdown = convertHTML(html) + + r = {'results': [{'types': mispattributes['output'], + 'values':[str(markdown)]}]} + return r + + +def introspection(): + return mispattributes + + +def version(): + return moduleinfo From 88c8d9077c3255c9c380b7cf623f9139ab30e9c2 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sat, 24 Oct 2020 02:40:31 +0200 Subject: [PATCH 201/220] fix: [cpe] Typos and variable name issues fixed + Making the module available in MISP --- misp_modules/modules/expansion/__init__.py | 4 ++-- misp_modules/modules/expansion/cpe.py | 23 ++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 1b6d2bb..fcd7774 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -5,8 +5,8 @@ import sys sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/')[:-3]))) __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'circl_passivessl', - 'countrycode', 'cve', 'cve_advanced', 'dns', 'btc_steroids', 'domaintools', 'eupi', 'eql', - 'farsight_passivedns', 'ipasn', 'passivetotal', 'sourcecache', 'virustotal', + 'countrycode', 'cve', 'cve_advanced', 'cpe', 'dns', 'btc_steroids', 'domaintools', 'eupi', + 'eql', 'farsight_passivedns', 'ipasn', 'passivetotal', 'sourcecache', 'virustotal', 'whois', 'shodan', 'reversedns', 'geoip_asn', 'geoip_city', 'geoip_country', 'wiki', 'iprep', 'threatminer', 'otx', 'threatcrowd', 'vulndb', 'crowdstrike_falcon', 'yara_syntax_validator', 'hashdd', 'onyphe', 'onyphe_full', 'rbl', diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 74cb1b6..9298876 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -1,19 +1,21 @@ import json import requests from . import check_input_attribute, standard_error_message +from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'} +mispattributes = {'input': ['cpe'], 'format': 'misp_standard'} moduleinfo = { 'version': '1', 'author': 'Christian Studer', - 'description': 'An expansion module to enrich a CPE attribute with the related vulnerabilities.', + 'description': 'An expansion module to enrich a CPE attribute with its related vulnerabilities.', 'module-type': ['expansion', 'hover'] } -moduleconfig = ["custom_API_URL"] +moduleconfig = ["custom_API_URL", "limit"] +cveapi_url = 'https://cve.circl.lu/api/cvefor/' -class VulnerabilityParser(): +class VulnerabilitiesParser(): def __init__(self, attribute, api_url): self.attribute = attribute self.api_url = api_url @@ -60,7 +62,7 @@ class VulnerabilityParser(): for feature in ('id', 'summary', 'Modified', 'Published', 'cvss'): if vulnerability.get(feature): attribute = {'value': vulnerability[feature]} - atttribute.update(self.vulnerability_mapping[feature]) + attribute.update(self.vulnerability_mapping[feature]) vulnerability_object.add_attribute(**attribute) if vulnerability.get('Published'): vulnerability_object.add_attribute(**{ @@ -81,7 +83,7 @@ class VulnerabilityParser(): def get_result(self): event = json.loads(self.misp_event.to_json()) - results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} @@ -98,10 +100,11 @@ def handler(q=False): attribute = request['attribute'] if attribute.get('type') != 'cpe': return {'error': 'Wrong input attribute type.'} - if not request.get('config') or not request['config'].get('custom_API_URL'): - return {'error': 'Missing API URL'} - api_url = check_url(request['config']['custom_API_URL']) - response = requests.get("{}{}".format(api_url, attribute['value'])) + api_url = check_url(request['config']['custom_API_URL']) if request['config'].get('custom_API_URL') else cveapi_url + url = f"{api_url}{attribute['value']}" + if request['config'].get('limit'): + url = f"{url}/{request['config']['limit']}" + response = requests.get(url) if response.status_code == 200: vulnerabilities = response.json() if not vulnerabilities: From 6660e2fc11748f61d35dc2542ac088b1d4341f57 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sat, 24 Oct 2020 23:52:06 +0200 Subject: [PATCH 202/220] add: Added documentation for the cpe module --- README.md | 1 + doc/README.md | 21 +++++++++++++++++++++ misp_modules/modules/expansion/cpe.py | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26dce03..6a38688 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate(s) seen. * [countrycode](misp_modules/modules/expansion/countrycode.py) - a hover module to tell you what country a URL belongs to. * [CrowdStrike Falcon](misp_modules/modules/expansion/crowdstrike_falcon.py) - an expansion module to expand using CrowdStrike Falcon Intel Indicator API. +* [CPE](misp_modules/modules/expansion/cpe.py) - An expansion module to query the CVE Search API with a cpe code, to get its related vulnerabilities. * [CVE](misp_modules/modules/expansion/cve.py) - a hover module to give more information about a vulnerability (CVE). * [CVE advanced](misp_modules/modules/expansion/cve_advanced.py) - An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE). * [Cuckoo submit](misp_modules/modules/expansion/cuckoo_submit.py) - A hover module to submit malware sample, url, attachment, domain to Cuckoo Sandbox. diff --git a/doc/README.md b/doc/README.md index 1225780..2245c3c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -222,6 +222,27 @@ Module to expand country codes. ----- +#### [cpe](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cpe.py) + + + +An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities. +- **features**: +>The module takes a cpe attribute as input and queries the CVE search API to get its related vulnerabilities. +>The list of vulnerabilities is then parsed and returned as vulnerability objects. +> +>Users can use their own CVE search API url by defining a value to the custom_API_URL parameter. If no custom API url is given, the default cve.circl.lu api url is used. +> +>In order to limit the amount of data returned by CVE serach, users can also the limit parameter. With the limit set, the API returns only the requested number of vulnerabilities, sorted from the highest cvss score to the lowest one. +- **input**: +>CPE attribute. +- **output**: +>The vulnerabilities related to the CPE. +- **references**: +>https://cve.circl.lu/api/ + +----- + #### [crowdstrike_falcon](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/crowdstrike_falcon.py) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 9298876..8ea0f65 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -88,7 +88,7 @@ class VulnerabilitiesParser(): def check_url(url): - return "{}/".format(url) if not url.endswith('/') else url + return url if url.endswith('/') else f"{url}/" def handler(q=False): From 06d93101b1ddc3ecb820884c14341cfd5f114c11 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 29 Oct 2020 18:03:25 +0100 Subject: [PATCH 203/220] add: Documentation for the html_to_markdown expansion module --- README.md | 1 + doc/README.md | 14 ++++++++++++++ doc/expansion/html_to_markdown.json | 7 +++++++ 3 files changed, 22 insertions(+) create mode 100644 doc/expansion/html_to_markdown.json diff --git a/README.md b/README.md index 26dce03..354100f 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. * [hashdd](misp_modules/modules/expansion/hashdd.py) - a hover module to check file hashes against [hashdd.com](http://www.hashdd.com) including NSLR dataset. * [hibp](misp_modules/modules/expansion/hibp.py) - a hover module to lookup against Have I Been Pwned? +* [html_to_markdown](misp_modules/modules/expansion/html_to_markdown) - Simple HTML to markdown converter * [intel471](misp_modules/modules/expansion/intel471.py) - an expansion module to get info from [Intel471](https://intel471.com). * [IPASN](misp_modules/modules/expansion/ipasn.py) - a hover and expansion to get the BGP ASN of an IP address. * [iprep](misp_modules/modules/expansion/iprep.py) - an expansion module to get IP reputation from packetmail.net. diff --git a/doc/README.md b/doc/README.md index 1225780..dc81b6b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -618,6 +618,20 @@ Module to access haveibeenpwned.com API. ----- +#### [html_to_markdown](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/html_to_markdown.py) + +Expansion module to fetch the html content from an url and convert it into markdown. +- **features**: +>The module take an URL as input and the HTML content is fetched from it. This content is then converted into markdown that is returned as text. +- **input**: +>URL attribute. +- **output**: +>Markdown content converted from the HTML fetched from the url. +- **requirements**: +>The markdownify python library + +----- + #### [intel471](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/intel471.py) diff --git a/doc/expansion/html_to_markdown.json b/doc/expansion/html_to_markdown.json new file mode 100644 index 0000000..45f6596 --- /dev/null +++ b/doc/expansion/html_to_markdown.json @@ -0,0 +1,7 @@ +{ + "description": "Expansion module to fetch the html content from an url and convert it into markdown.", + "input": "URL attribute.", + "output": "Markdown content converted from the HTML fetched from the url.", + "requirements": ["The markdownify python library"], + "features": "The module take an URL as input and the HTML content is fetched from it. This content is then converted into markdown that is returned as text." +} From e4d2f9076737d32b11f5fc5f399277a2f701ccfc Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 29 Oct 2020 18:22:07 +0100 Subject: [PATCH 204/220] fix: Updated Pipfile --- Pipfile | 1 + Pipfile.lock | 619 +++++++++++++++++++++++++++++---------------------- 2 files changed, 354 insertions(+), 266 deletions(-) diff --git a/Pipfile b/Pipfile index 1169368..a79e260 100644 --- a/Pipfile +++ b/Pipfile @@ -61,6 +61,7 @@ apiosintDS = "*" assemblyline_client = "*" vt-graph-api = "*" trustar = "*" +markdownify = "==0.5.3" [requires] python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock index 1229e98..bc00dee 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c2d937b384431e4b313b29bb02db0bd1d3a866ddcb7c6e91cbfa34f88d351b59" + "sha256": "f707a3cd3025e6d0182d2a440138a9c3ab0810632aedc308274c6f9437489a87" }, "pipfile-spec": 6, "requires": { @@ -18,21 +18,42 @@ "default": { "aiohttp": { "hashes": [ - "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", - "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", - "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", - "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", - "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", - "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", - "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", - "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", - "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", - "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", - "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", - "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" + "sha256:027be45c4b37e21be81d07ae5242361d73eebad1562c033f80032f955f34df82", + "sha256:06efdb01ab71ec20786b592d510d1d354fbe0b2e4449ee47067b9ca65d45a006", + "sha256:0989ff15834a4503056d103077ec3652f9ea5699835e1ceaee46b91cf59830bf", + "sha256:11e087c316e933f1f52f3d4a09ce13f15ad966fc43df47f44ca4e8067b6a2e0d", + "sha256:184ead67248274f0e20b0cd6bb5f25209b2fad56e5373101cc0137c32c825c87", + "sha256:1c36b7ef47cfbc150314c2204cd73613d96d6d0982d41c7679b7cdcf43c0e979", + "sha256:2aea79734ac5ceeac1ec22b4af4efb4efd6a5ca3d73d77ec74ed782cf318f238", + "sha256:2e886611b100c8c93b753b457e645c5e4b8008ec443434d2a480e5a2bb3e6514", + "sha256:476b1f8216e59a3c2ffb71b8d7e1da60304da19f6000d422bacc371abb0fc43d", + "sha256:48104c883099c0e614c5c38f98c1d174a2c68f52f58b2a6e5a07b59df78262ab", + "sha256:4afd8002d9238e5e93acf1a8baa38b3ddf1f7f0ebef174374131ff0c6c2d7973", + "sha256:547b196a7177511da4f475fc81d0bb88a51a8d535c7444bbf2338b6dc82cb996", + "sha256:67f8564c534d75c1d613186939cee45a124d7d37e7aece83b17d18af665b0d7a", + "sha256:6e0d1231a626d07b23f6fe904caa44efb249da4222d8a16ab039fb2348722292", + "sha256:7e26712871ebaf55497a60f55483dc5e74326d1fb0bfceab86ebaeaa3a266733", + "sha256:7f1aeb72f14b9254296cdefa029c00d3c4550a26e1059084f2ee10d22086c2d0", + "sha256:8319a55de469d5af3517dfe1f6a77f248f6668c5a552396635ef900f058882ef", + "sha256:835bd35e14e4f36414e47c195e6645449a0a1c3fd5eeae4b7f22cb4c5e4f503a", + "sha256:89c1aa729953b5ac6ca3c82dcbd83e7cdecfa5cf9792c78c154a642e6e29303d", + "sha256:8a8addd41320637c1445fea0bae1fd9fe4888acc2cd79217ee33e5d1c83cfe01", + "sha256:8fbeeb2296bb9fe16071a674eadade7391be785ae0049610e64b60ead6abcdd7", + "sha256:a1f1cc11c9856bfa7f1ca55002c39070bde2a97ce48ef631468e99e2ac8e3fe6", + "sha256:ad5c3559e3cd64f746df43fa498038c91aa14f5d7615941ea5b106e435f3b892", + "sha256:b822bf7b764283b5015e3c49b7bb93f37fc03545f4abe26383771c6b1c813436", + "sha256:b84cef790cb93cec82a468b7d2447bf16e3056d2237b652e80f57d653b61da88", + "sha256:be9fa3fe94fc95e9bf84e84117a577c892906dd3cb0a95a7ae21e12a84777567", + "sha256:c53f1d2bd48f5f407b534732f5b3c6b800a58e70b53808637848d8a9ee127fe7", + "sha256:c588a0f824dc7158be9eec1ff465d1c868ad69a4dc518cd098cc11e4f7da09d9", + "sha256:c6da1af59841e6d43255d386a2c4bfb59c0a3b262bdb24325cc969d211be6070", + "sha256:c9a415f4f2764ab6c7d63ee6b86f02a46b4df9bc11b0de7ffef206908b7bf0b4", + "sha256:cdbb65c361ff790c424365a83a496fc8dd1983689a5fb7c6852a9a3ff1710c61", + "sha256:f04dcbf6af1868048a9b4754b1684c669252aa2419aa67266efbcaaead42ced7", + "sha256:f8c583c31c6e790dc003d9d574e3ed2c5b337947722965096c4d684e4f183570" ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.6.2" + "markers": "python_version >= '3.6'", + "version": "==3.7.2" }, "antlr4-python3-runtime": { "hashes": [ @@ -174,11 +195,11 @@ }, "colorama": { "hashes": [ - "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", - "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", + "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.4.3" + "version": "==0.4.4" }, "configparser": { "hashes": [ @@ -190,30 +211,30 @@ }, "cryptography": { "hashes": [ - "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499", - "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154", - "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6", - "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49", - "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f", - "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396", - "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719", - "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db", - "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70", - "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536", - "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe", - "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba", - "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d", - "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7", - "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490", - "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8", - "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921", - "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118", - "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba", - "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3", - "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc", - "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2" + "sha256:07ca431b788249af92764e3be9a488aa1d39a0bc3be313d826bbec690417e538", + "sha256:13b88a0bd044b4eae1ef40e265d006e34dbcde0c2f1e15eb9896501b2d8f6c6f", + "sha256:32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77", + "sha256:3cd75a683b15576cfc822c7c5742b3276e50b21a06672dc3a800a2d5da4ecd1b", + "sha256:4e7268a0ca14536fecfdf2b00297d4e407da904718658c1ff1961c713f90fd33", + "sha256:545a8550782dda68f8cdc75a6e3bf252017aa8f75f19f5a9ca940772fc0cb56e", + "sha256:55d0b896631412b6f0c7de56e12eb3e261ac347fbaa5d5e705291a9016e5f8cb", + "sha256:5849d59358547bf789ee7e0d7a9036b2d29e9a4ddf1ce5e06bb45634f995c53e", + "sha256:6dc59630ecce8c1f558277ceb212c751d6730bd12c80ea96b4ac65637c4f55e7", + "sha256:7117319b44ed1842c617d0a452383a5a052ec6aa726dfbaffa8b94c910444297", + "sha256:75e8e6684cf0034f6bf2a97095cb95f81537b12b36a8fedf06e73050bb171c2d", + "sha256:7b8d9d8d3a9bd240f453342981f765346c87ade811519f98664519696f8e6ab7", + "sha256:a035a10686532b0587d58a606004aa20ad895c60c4d029afa245802347fab57b", + "sha256:a4e27ed0b2504195f855b52052eadcc9795c59909c9d84314c5408687f933fc7", + "sha256:a733671100cd26d816eed39507e585c156e4498293a907029969234e5e634bc4", + "sha256:a75f306a16d9f9afebfbedc41c8c2351d8e61e818ba6b4c40815e2b5740bb6b8", + "sha256:bd717aa029217b8ef94a7d21632a3bb5a4e7218a4513d2521c2a2fd63011e98b", + "sha256:d25cecbac20713a7c3bc544372d42d8eafa89799f492a43b79e1dfd650484851", + "sha256:d26a2557d8f9122f9bf445fc7034242f4375bd4e95ecda007667540270965b13", + "sha256:d3545829ab42a66b84a9aaabf216a4dce7f16dbc76eb69be5c302ed6b8f4a29b", + "sha256:d3d5e10be0cf2a12214ddee45c6bd203dab435e3d83b4560c03066eda600bfe3", + "sha256:efe15aca4f64f3a7ea0c09c87826490e50ed166ce67368a68f315ea0807a20df" ], - "version": "==3.1.1" + "version": "==3.2.1" }, "decorator": { "hashes": [ @@ -320,10 +341,10 @@ }, "jbxapi": { "hashes": [ - "sha256:8458f01a9b4e4245d61f6fa75edef17e2992192975f746c51ed5392ba9aa7ce5" + "sha256:0605208a072ff5752754df0798f0de5acd8630e37237e04f816f1393c2c08b80" ], "index": "pypi", - "version": "==3.11.0" + "version": "==3.13.0" }, "json-log-formatter": { "hashes": [ @@ -359,40 +380,45 @@ }, "lxml": { "hashes": [ - "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f", - "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730", - "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f", - "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1", - "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3", - "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7", - "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a", - "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe", - "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1", - "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e", - "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d", - "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20", - "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae", - "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5", - "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba", - "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293", - "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a", - "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6", - "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88", - "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed", - "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843", - "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443", - "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0", - "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304", - "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258", - "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6", - "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1", - "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481", - "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef", - "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd", - "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee" + "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b", + "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5", + "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301", + "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b", + "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d", + "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b", + "sha256:24e811118aab6abe3ce23ff0d7d38932329c513f9cef849d3ee88b0f848f2aa9", + "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b", + "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311", + "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891", + "sha256:302160eb6e9764168e01d8c9ec6becddeb87776e81d3fcb0d97954dd51d48e0a", + "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1", + "sha256:3d9b2b72eb0dbbdb0e276403873ecfae870599c83ba22cadff2db58541e72856", + "sha256:475325e037fdf068e0c2140b818518cf6bc4aa72435c407a798b2db9f8e90810", + "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51", + "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360", + "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4", + "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f", + "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230", + "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a", + "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f", + "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174", + "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf", + "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd", + "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3", + "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a", + "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5", + "sha256:c152b2e93b639d1f36ec5a8ca24cde4a8eefb2b6b83668fcd8e83a67badcb367", + "sha256:d182eada8ea0de61a45a526aa0ae4bcd222f9673424e65315c35820291ff299c", + "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1", + "sha256:d20d32cbb31d731def4b1502294ca2ee99f9249b63bc80e03e67e8f8e126dea8", + "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f", + "sha256:d6f8c23f65a4bfe4300b85f1f40f6c32569822d08901db3b6454ab785d9117cc", + "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d", + "sha256:e65c221b2115a91035b55a593b6eb94aa1206fa3ab374f47c6dc10d364583ff9", + "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f" ], "index": "pypi", - "version": "==4.5.2" + "version": "==4.6.1" }, "maclookup": { "hashes": [ @@ -402,12 +428,20 @@ "index": "pypi", "version": "==1.0.3" }, + "markdownify": { + "hashes": [ + "sha256:30be8340724e706c9e811c27fe8c1542cf74a15b46827924fff5c54b40dd9b0d", + "sha256:a69588194fd76634f0139d6801b820fd652dc5eeba9530e90d323dfdc0155252" + ], + "index": "pypi", + "version": "==0.5.3" + }, "maxminddb": { "hashes": [ - "sha256:b95d8ed21799e6604683669c7ed3c6a184fcd92434d5762dccdb139b4f29e597" + "sha256:47e86a084dd814fac88c99ea34ba3278a74bc9de5a25f4b815b608798747c7dc" ], "markers": "python_version >= '3.6'", - "version": "==2.0.2" + "version": "==2.0.3" }, "misp-modules": { "editable": true, @@ -415,26 +449,42 @@ }, "multidict": { "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" + "sha256:02b2ea2bb1277a970d238c5c783023790ca94d386c657aeeb165259950951cc6", + "sha256:0ce1d956ecbf112d49915ebc2f29c03e35fe451fb5e9f491edf9a2f4395ee0af", + "sha256:0ffdb4b897b15df798c0a5939a0323ccf703f2bae551dfab4eb1af7fbab38ead", + "sha256:11dcf2366da487d5b9de1d4b2055308c7ed9bde1a52973d07a89b42252af9ebe", + "sha256:167bd8e6351b57525bbf2d524ca5a133834699a2fcb090aad0c330c6017f3f3e", + "sha256:1b324444299c3a49b601b1bf621fc21704e29066f6ac2b7d7e4034a4a18662a1", + "sha256:20eaf1c279c543e07c164e4ac02151488829177da06607efa7ccfecd71b21e79", + "sha256:2739d1d9237835122b27d88990849ecf41ef670e0fcb876159edd236ca9ef40f", + "sha256:28b5913e5b6fef273e5d4230b61f33c8a51c3ce5f44a88582dee6b5ca5c9977b", + "sha256:2b0cfc33f53e5c8226f7d7c4e126fa0780f970ef1e96f7c6353da7d01eafe490", + "sha256:32f0a904859a6274d7edcbb01752c8ae9c633fb7d1c131771ff5afd32eceee42", + "sha256:39713fa2c687e0d0e709ad751a8a709ac051fcdc7f2048f6fd09365dd03c83eb", + "sha256:4ef76ce695da72e176f6a51867afb3bf300ce16ba2597824caaef625af5906a9", + "sha256:5263359a03368985b5296b7a73363d761a269848081879ba04a6e4bfd0cf4a78", + "sha256:52b5b51281d760197ce3db063c166fdb626e01c8e428a325aa37198ce31c9565", + "sha256:5dd303b545b62f9d2b14f99fbdb84c109a20e64a57f6a192fe6aebcb6263b59d", + "sha256:60af726c19a899ed49bbb276e062f08b80222cb6b9feda44b59a128b5ff52966", + "sha256:60b12d14bc122ba2dae1e4460a891b3a96e73d815b4365675f6ec0a1725416a5", + "sha256:620c39b1270b68e194023ad471b6a54bdb517bb48515939c9829b56c783504a3", + "sha256:62f6e66931fb87e9016e7c1cc806ab4f3e39392fd502362df3cac888078b27cb", + "sha256:711289412b78cf41a21457f4c806890466013d62bf4296bd3d71fad73ff8a581", + "sha256:7561a804093ea4c879e06b5d3d18a64a0bc21004bade3540a4b31342b528d326", + "sha256:786ad04ad954afe9927a1b3049aa58722e182160fe2fcac7ad7f35c93595d4f6", + "sha256:79dc3e6e7ce853fb7ed17c134e01fcb0d0c826b33201aa2a910fb27ed75c2eb9", + "sha256:84e4943d8725659942e7401bdf31780acde9cfdaf6fe977ff1449fffafcd93a9", + "sha256:932964cf57c0e59d1f3fb63ff342440cf8aaa75bf0dbcbad902c084024975380", + "sha256:a5eca9ee72b372199c2b76672145e47d3c829889eefa2037b1f3018f54e5f67d", + "sha256:aad240c1429e386af38a2d6761032f0bec5177fed7c5f582c835c99fff135b5c", + "sha256:bbec545b8f82536bc50afa9abce832176ed250aa22bfff3e20b3463fb90b0b35", + "sha256:c339b7d73c0ea5c551025617bb8aa1c00a0111187b6545f48836343e6cfbe6a0", + "sha256:c692087913e12b801a759e25a626c3d311f416252dfba2ecdfd254583427949f", + "sha256:cda06c99cd6f4a36571bb38e560a6fcfb1f136521e57f612e0bc31957b1cd4bd", + "sha256:ec8bc0ab00c76c4260a201eaa58812ea8b1b7fde0ecf5f9c9365a182bd4691ed" ], "markers": "python_version >= '3.5'", - "version": "==4.7.6" + "version": "==5.0.0" }, "np": { "hashes": [ @@ -445,35 +495,43 @@ }, "numpy": { "hashes": [ - "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5", - "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8", - "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf", - "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c", - "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65", - "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716", - "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a", - "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e", - "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b", - "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45", - "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d", - "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d", - "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02", - "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c", - "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526", - "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15", - "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d", - "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a", - "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1", - "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a", - "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1", - "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9", - "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4", - "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c", - "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd", - "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8" + "sha256:0ee77786eebbfa37f2141fd106b549d37c89207a0d01d8852fde1c82e9bfc0e7", + "sha256:199bebc296bd8a5fc31c16f256ac873dd4d5b4928dfd50e6c4995570fc71a8f3", + "sha256:1a307bdd3dd444b1d0daa356b5f4c7de2e24d63bdc33ea13ff718b8ec4c6a268", + "sha256:1ea7e859f16e72ab81ef20aae69216cfea870676347510da9244805ff9670170", + "sha256:271139653e8b7a046d11a78c0d33bafbddd5c443a5b9119618d0652a4eb3a09f", + "sha256:35bf5316af8dc7c7db1ad45bec603e5fb28671beb98ebd1d65e8059efcfd3b72", + "sha256:463792a249a81b9eb2b63676347f996d3f0082c2666fd0604f4180d2e5445996", + "sha256:50d3513469acf5b2c0406e822d3f314d7ac5788c2b438c24e5dd54d5a81ef522", + "sha256:50f68ebc439821b826823a8da6caa79cd080dee2a6d5ab9f1163465a060495ed", + "sha256:51e8d2ae7c7e985c7bebf218e56f72fa93c900ad0c8a7d9fbbbf362f45710f69", + "sha256:522053b731e11329dd52d258ddf7de5288cae7418b55e4b7d32f0b7e31787e9d", + "sha256:5ea4401ada0d3988c263df85feb33818dc995abc85b8125f6ccb762009e7bc68", + "sha256:604d2e5a31482a3ad2c88206efd43d6fcf666ada1f3188fd779b4917e49b7a98", + "sha256:6ff88bcf1872b79002569c63fe26cd2cda614e573c553c4d5b814fb5eb3d2822", + "sha256:7197ee0a25629ed782c7bd01871ee40702ffeef35bc48004bc2fdcc71e29ba9d", + "sha256:741d95eb2b505bb7a99fbf4be05fa69f466e240c2b4f2d3ddead4f1b5f82a5a5", + "sha256:83af653bb92d1e248ccf5fdb05ccc934c14b936bcfe9b917dc180d3f00250ac6", + "sha256:8802d23e4895e0c65e418abe67cdf518aa5cbb976d97f42fd591f921d6dffad0", + "sha256:8edc4d687a74d0a5f8b9b26532e860f4f85f56c400b3a98899fc44acb5e27add", + "sha256:942d2cdcb362739908c26ce8dd88db6e139d3fa829dd7452dd9ff02cba6b58b2", + "sha256:9a0669787ba8c9d3bb5de5d9429208882fb47764aa79123af25c5edc4f5966b9", + "sha256:9d08d84bb4128abb9fbd9f073e5c69f70e5dab991a9c42e5b4081ea5b01b5db0", + "sha256:9f7f56b5e85b08774939622b7d45a5d00ff511466522c44fc0756ac7692c00f2", + "sha256:a2daea1cba83210c620e359de2861316f49cc7aea8e9a6979d6cb2ddab6dda8c", + "sha256:b9074d062d30c2779d8af587924f178a539edde5285d961d2dfbecbac9c4c931", + "sha256:c4aa79993f5d856765819a3651117520e41ac3f89c3fc1cb6dee11aa562df6da", + "sha256:d78294f1c20f366cde8a75167f822538a7252b6e8b9d6dbfb3bdab34e7c1929e", + "sha256:dfdc8b53aa9838b9d44ed785431ca47aa3efaa51d0d5dd9c412ab5247151a7c4", + "sha256:dffed17848e8b968d8d3692604e61881aa6ef1f8074c99e81647ac84f6038535", + "sha256:e080087148fd70469aade2abfeadee194357defd759f9b59b349c6192aba994c", + "sha256:e983cbabe10a8989333684c98fdc5dd2f28b236216981e0c26ed359aaa676772", + "sha256:ea6171d2d8d648dee717457d0f75db49ad8c2f13100680e284d7becf3dc311a6", + "sha256:eefc13863bf01583a85e8c1121a901cc7cb8f059b960c4eba30901e2e6aba95f", + "sha256:efd656893171bbf1331beca4ec9f2e74358fc732a2084f664fd149cc4b3441d2" ], "markers": "python_version >= '3.6'", - "version": "==1.19.2" + "version": "==1.19.3" }, "oauth2": { "hashes": [ @@ -512,24 +570,29 @@ }, "pandas": { "hashes": [ - "sha256:206d7c3e5356dcadf082e64dc25c24bc8541718045826074f96346e9d6d05a20", - "sha256:24f61f40febe47edac271eda45d683e42838b7db2bd0f82574d9800259d2b182", + "sha256:ca71a5aa9eeb3ef5b31feca7d9b6369d6b3d0b2e9c85d7a89abe3ecb013f1e86", + "sha256:df43ea0e9fd9f9672b0de9cac26d01255ad50481994bf3cb4687c21eec2d7bbc", + "sha256:babbeda2f83b0686c9ad38d93b10516e68cdcd5771007eb80a763e98aaf44613", + "sha256:147162568b1242355290341baf281926cfac66ada07e634f3fc521ac967e4653", + "sha256:d6b1f9d506dc23da2915bcae5c5968990049c9cec44108bd9855d2c7c89d91dc", "sha256:3a038cd5da602b955d335aa80cbaa0e5774f68501ff47b9c21509906981478da", - "sha256:427be9938b2f79ab298de84f87693914cda238a27cf10580da96caf3dff64115", - "sha256:54f5f564058b0280d588c3758abde82e280702c440db5faf0c686b80336096f9", - "sha256:5a8a84b75ca3a29bb4263b35d5ed9fcaae2b062f014feed8c5daa897339c7d85", "sha256:84a4ffe668df357e31f98c829536e3a7142c3036c82f996e639f644c5d32eda1", "sha256:882012763668af54b48f1412bab95c5cc0a7ccce5a2a8221cfc3839a6e3394ef", - "sha256:920d30fdff65a079f071db635d282b4f583c2b26f2b58d5dca218aac7c59974d", - "sha256:a605054fbca71ed1d08bb2aef6f73c84a579bbac956bfe8f9718d5e84cb41248", + "sha256:d89dbc58aec1544722a8d5046f880b597c497ef8a82c5fe695b4b2effafac5ec", + "sha256:5a8a84b75ca3a29bb4263b35d5ed9fcaae2b062f014feed8c5daa897339c7d85", + "sha256:24f61f40febe47edac271eda45d683e42838b7db2bd0f82574d9800259d2b182", "sha256:b11b496c317dbe007898de699fd59eaf687d0fe8c1b7dad109db6010155d28ae", - "sha256:babbeda2f83b0686c9ad38d93b10516e68cdcd5771007eb80a763e98aaf44613", + "sha256:206d7c3e5356dcadf082e64dc25c24bc8541718045826074f96346e9d6d05a20", "sha256:c22e40f1b4d162ca18eb6b2c572e63eef220dbc9cc3de0241cefb77972621bb7", "sha256:ca31ac8578d48da354cf66a473d4d5ff99277ca71d321dc7ea4e6fad3c6bb0fd", - "sha256:ca71a5aa9eeb3ef5b31feca7d9b6369d6b3d0b2e9c85d7a89abe3ecb013f1e86", - "sha256:d6b1f9d506dc23da2915bcae5c5968990049c9cec44108bd9855d2c7c89d91dc", - "sha256:d89dbc58aec1544722a8d5046f880b597c497ef8a82c5fe695b4b2effafac5ec", - "sha256:df43ea0e9fd9f9672b0de9cac26d01255ad50481994bf3cb4687c21eec2d7bbc", + "sha256:a605054fbca71ed1d08bb2aef6f73c84a579bbac956bfe8f9718d5e84cb41248", + "sha256:f4cb8252ae71f093f4a6b847adf0bc9330f109c48f08363c2071f189f1c89c87", + "sha256:2999adc6736f8cb4c69d65a6e2b25a11bcb395da5b048342b8e4d6fe055e57ae", + "sha256:54f5f564058b0280d588c3758abde82e280702c440db5faf0c686b80336096f9", + "sha256:b026e913d88fad3a74eea8ed5a5f98e8823080ea02f8d9bb0ec19e92552daad6", + "sha256:11c284769f41e95f7d16a327eb555989c5f29418aad075fa80c97ef3aa8fb885", + "sha256:920d30fdff65a079f071db635d282b4f583c2b26f2b58d5dca218aac7c59974d", + "sha256:427be9938b2f79ab298de84f87693914cda238a27cf10580da96caf3dff64115", "sha256:fd6f05b6101d0e76f3e5c26a47be5be7be96ed84ef3981dc1852e76898e73594" ], "index": "pypi", @@ -561,37 +624,37 @@ }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", + "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", + "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", + "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", + "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", + "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", + "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", + "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", + "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", + "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", + "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", + "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", + "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", + "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", + "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", + "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", + "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", + "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792", + "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", + "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", + "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", + "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", + "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", + "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", + "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", + "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", + "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", + "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72" ], "index": "pypi", - "version": "==7.2.0" + "version": "==8.0.1" }, "progressbar2": { "hashes": [ @@ -602,20 +665,20 @@ }, "psutil": { "hashes": [ - "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8", - "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498", - "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6", - "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c", - "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195", - "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f", - "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb", - "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1", - "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf", - "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2", - "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818" + "sha256:01bc82813fbc3ea304914581954979e637bcc7084e59ac904d870d6eb8bb2bc7", + "sha256:1cd6a0c9fb35ece2ccf2d1dd733c1e165b342604c67454fd56a4c12e0a106787", + "sha256:2cb55ef9591b03ef0104bedf67cc4edb38a3edf015cf8cf24007b99cb8497542", + "sha256:56c85120fa173a5d2ad1d15a0c6e0ae62b388bfb956bb036ac231fbdaf9e4c22", + "sha256:5d9106ff5ec2712e2f659ebbd112967f44e7d33f40ba40530c485cc5904360b8", + "sha256:6a3e1fd2800ca45083d976b5478a2402dd62afdfb719b30ca46cd28bb25a2eb4", + "sha256:ade6af32eb80a536eff162d799e31b7ef92ddcda707c27bbd077238065018df4", + "sha256:af73f7bcebdc538eda9cc81d19db1db7bf26f103f91081d780bbacfcb620dee2", + "sha256:e02c31b2990dcd2431f4524b93491941df39f99619b0d312dfe1d4d530b08b4b", + "sha256:fa38ac15dbf161ab1e941ff4ce39abd64b53fec5ddf60c23290daed2bc7d1157", + "sha256:fbcac492cb082fa38d88587d75feb90785d05d7e12d4565cbf1ecc727aff71b7" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==5.7.2" + "version": "==5.7.3" }, "pybgpranking": { "editable": true, @@ -759,7 +822,7 @@ "pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "bacd4c78cd83d3bf45dcf55cd9ad3514747ac985" + "ref": "deb9e06c726592c145e44b25fa6a05db56e3aa80" }, "pyonyphe": { "editable": true, @@ -926,49 +989,49 @@ }, "reportlab": { "hashes": [ - "sha256:0145233d3596fa5828972eb474b5a9f3fd5dea45d6f196fe006a7a7a461fcd03", - "sha256:04fd4a129393006c4ba9cd9fff56b78ad60fe6702326e9260f55d4abac9f1df2", - "sha256:067800caa12ea69e8df0a9206a7eda6697f91a33edb8413b778647d270bc9f34", - "sha256:106a61093cf6084fbcb1272768f090b06137027e09c5e53c573c6c7b90216066", - "sha256:13afbdca2b0844c19ee6804220bb96630f44ffa2571781de66a04e3f83609295", - "sha256:155887770694a1febb4b1bcd2e2856c931225fa1fe8c5ef6772fce47c07f6204", - "sha256:17c906bc410f5eef01795d709ad88663ab98447683d21b6e97bac9b366504a8a", - "sha256:1880282b9a278b4df5139b2083b9116388d9e1fb4a438c60b3cc4ad983da1bc5", - "sha256:2248f9c362f417d108329fdf5083ede1914757534f1b255d6c37a9a6d99c5efe", - "sha256:2dc571be9d2fec76f8bddb540581429eb16057ff9101767d8b15166ad1de70db", - "sha256:35dda0a1994a8fc009bf5826fe34dcdb15e561b05a5a01c506d949accfbdf027", - "sha256:3858534058ab99fbedb34ceae31f85bbadeeb8e4dbb78a58927599a6f0422617", - "sha256:4710d237fe9f729eacbbb7477d14eea00781704e0cdb83c789e610365e40627f", - "sha256:49e32586d3a814a5f77407c0590504a72743ca278518b3c0f90182430f2d87af", - "sha256:4cdb2ab88839f0d36364b71744b742e09699bde9b943aa35da26580831c3f106", - "sha256:5e995f77124933d3e16ddc09f95ab36793083a1cb08ed2557811f8cfb254434b", - "sha256:73bc92579692609837fb13f271f7436fdb7b6ddebb9e10185452d45814c365c3", - "sha256:7931097db5f18e3ac6909a223e94dd3ad0258541f9802effa5b8f519ef9278e4", - "sha256:7eb3d96adb309593bded364d25a32b80f9dc18b2f9a4b2001972194027a77eef", - "sha256:886bdc7c13e6c6513696eb044000491c787fd53a486aa3adea060d34aa3cd028", - "sha256:8c242a2be8d71ff18e11938cf45114d1144544984cd34fea0606f04144d62bea", - "sha256:8f2759d2a81ee992054e7a1123cadd6baff4edecc1249e503bb6decd6b55e8ee", - "sha256:9765c0eec5e6927aaccf6bd460fe24a014d35a3979f2c7507644fd5946775921", - "sha256:9c7173def03fd3048f07bce00d4ca4793efc37239811d9b3eb77edb561363cd2", - "sha256:a1d0e20cae86c6ba5e6626a9e07eca4d298341adfee778f87d5837bc76912135", - "sha256:a5398e7af6136c25a34569132e7e2646c72a2f89e53028ef109fb03b5a2923a6", - "sha256:a690fe672aa51ee3a6ff4c96d2f5d9744d3b6f27c999a795b9c513923f875bfc", - "sha256:b18ea3593d4edc7f05c510ab298d48548d9a4473a643f37661b1669365d7d33c", - "sha256:b727050ec5dfc4baeded07199d4640156f360ff4624b0194d8e91b234fc0c26b", - "sha256:be53e8423f35d3c80b0560aec034226fdab5623bb4d64b962c3f04b65980b3e0", - "sha256:c70e9c9cfdc0596c3912e0d147f42e83c7ac5642ac82d6fe05d85a6326bae14d", - "sha256:ce7c13eb469f864085a546881a3bc9b46e20a73dc1a43b9e84153833e628dee3", - "sha256:d6bd4d59f4b558165f05f9f7dfad37b9d788bcc05c0b37a6b0fcb6165d6893ec", - "sha256:d75114965cc84ee51aaf3d7eda90f3554f3ac67350ebacd1dbb9193a7a525e21", - "sha256:d78fdb967bd7652515d9a23ff3088e32e32ef96332737696e9eb0fda5602bf81", - "sha256:d930a3de0fa9711b9c960dee92ff2b30c3f69568f00f0244834fe28d5563ea9b", - "sha256:e32af1e47076a3fc77e6be5f7e2c8cbbc82fe493a5cd3f6190c0f8980c401e59", - "sha256:e50de7d196f2d3940f3fdea0f30bf67929686d57285b3779fb071d05a810d65f", - "sha256:e7b7e4a0ce0f455a4777528a8a316e87cc6cf887eaa2a4e6a0cc103f031c57c2", - "sha256:e8dd01462a1bb41b6806aa93a703100d3fbba760f8feca96fcec710db9384a25" + "sha256:00b9b3ffbd197b21cb076acc336993005b75d16b60f7a79a3c8faee926f890b7", + "sha256:0177b58d0ae81f6775b10e66f97bc7aa490659398e1f24401b6d1767803c4880", + "sha256:03c792a92ba21e75e05230ef1ce038025c23b124c706d7369dfa1475a0d24785", + "sha256:04044318273fa00487557f2e79bb6f8faa08185b8b1795cc29985ccb609c8680", + "sha256:217da82e7451e2b101a4bd72006a7e6c0d3203200cfb5c4d6a17b997b9ba73c6", + "sha256:226b5ef9af16aa8b3487513556ae7386239fe3ec8b121b1e23f45b850f0a10a8", + "sha256:24773aba8c74e1e023a1d3c3c60dbd6ef4a76472e38f13b5a214c8bb48db7aef", + "sha256:25eb9bb45e206b3a464f763d1231d70bb5f351c01d5ab94568e687fec4bd9eee", + "sha256:3d6d26294e8e3f6a639ee4a4b423d2cb0fa7de24c4cccea50a32d50d20db52ad", + "sha256:4987cca329df7f9bf4b6abea3e83c26a5a8edfe5b133344e24f146ddc8c09b9a", + "sha256:4b6a7e9a83e00cfe020c8e8bdd595384312228b24dcb40538d5cf00df15c5bff", + "sha256:531b70748dd89456c4e1d2132497bc8580ac74d7fcb790b8e2d1b20378655ba2", + "sha256:57abf06c045d16a85906fbdd8d826d7e334377bbb29b7442d249a95cf5f3a5c5", + "sha256:58877ed7390327bf4c41ca75473223866f7d8da0f8a606eb682127c8ac4af990", + "sha256:640d41838b1e663c5db53f3c32294cd742ac5cc4ba3098aeaad53297b7e1cc47", + "sha256:658471d5b06e121692449f44a4e39e3c7128fea757c4e9354b488f35ac3f82de", + "sha256:6f971a53e02682866886c451513143f46aed65704e46327bb6440604cd7cd7eb", + "sha256:78dcf1aff25ddf68b147e78b074bef1384e804dd54322eb1d1f1f680892f8788", + "sha256:793ed7edd50306cd05213ac012749dfe65768485bd493c3434936438d594a363", + "sha256:7a3512585308e5c73bf123457ccfc90acb99493df89fae6131caaec9ffe1e4ca", + "sha256:7ace84b3aae39b14ce7235d096bc81891f60b871b7edad2b656cb1729100e0f2", + "sha256:7e84d123ec98816fce5a97af2755d664519e7891e9793330ec271900acb2bfab", + "sha256:813c31d8b7f28ee2f38f238c3eb6afb02b81b00d749ab10e38b534843680aea7", + "sha256:8365efe779e43e8005eace19c11c36e6a4bbea86ddc868b8db122240391c1747", + "sha256:8412514dc0d1bf62c6b33a645b5a7c46933cc16f3678db5546d0ac4e27f3dbae", + "sha256:8d4ba2aea71ab6ec688b3f3416db0d457e7814a642433b7f407a3f29e054816d", + "sha256:99a7cdd8633a8717dd239917647b42d9a6b869a01c39019c7b0b08b963be2a7e", + "sha256:9d86fe83e9c4838e0048f14067869d1ca8722bb52545781db7a9d345939e77f0", + "sha256:a626a97ab135f2129d87c5f98b2aee45e0ef1652bc9afef92509a8f5a5f72e45", + "sha256:a921906c1deb199f7910163703e4073b52e8d7f00d56d4f6bbc255a6ca3cfb1d", + "sha256:b80840cc4fece1426d30070a9dad016d9589e8d82ebddfc9ed30004b44ba2803", + "sha256:c5318b4e23803c7c5f2b7384858b7b6be5faf51f63664c97f6bf8601cd248855", + "sha256:cd5546d840f639587f352d4c54ff35422cbeba81eb2c50d156cd733015ecc4b2", + "sha256:d445fc4ada6a24a90080f7379d169fba1072ba5a75179ce2f5c3280adf605b45", + "sha256:d56f150bb4b2d32596291aa98d3c6986721c5cf41b8f90346a84cee8b7fb35f2", + "sha256:d6e42636247e4c6d2db929b9db01d1af907f63aa74af8123cd699107df8a7b23", + "sha256:dcf732695b1325289a9a74b849179d8475db32a00803644a664c2172a603237e", + "sha256:e7b20927e5e11bad8bac5d5b6c286ce2cae2804073513aa67f20986bc4b3b4e0", + "sha256:f6295876665359790dcb7042a9221c60e1f89dee042f33414e3ce440772f7aa1", + "sha256:f8ec6637f56c293ac62c9a94daebb856c4ef9b97eae4cf7b4e518813e41c8c75" ], "index": "pypi", - "version": "==3.5.53" + "version": "==3.5.54" }, "requests": { "extras": [ @@ -990,10 +1053,10 @@ }, "shodan": { "hashes": [ - "sha256:d2d37d47dd084747df672e6d981f6d72d5d03f4ee12f0ce2170e618147578349" + "sha256:0b5ec40c954cd48c4e3234e81ad92afdc68438f82ad392fed35b7097eb77b6dd" ], "index": "pypi", - "version": "==1.23.1" + "version": "==1.24.0" }, "sigmatools": { "hashes": [ @@ -1068,10 +1131,18 @@ }, "trustar": { "hashes": [ - "sha256:47c45674a4a310dc8d932035e0de112de55c1e899663865b996a6b6b2d79cbde" + "sha256:2618a377e3c000a41a47eb34b31ea694215eed4a1d2e3cfca1801ac6baebd958" ], "index": "pypi", - "version": "==0.3.33" + "version": "==0.3.34" + }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "version": "==3.7.4.3" }, "tzlocal": { "hashes": [ @@ -1088,11 +1159,11 @@ }, "url-normalize": { "hashes": [ - "sha256:1709cb4739e496f9f807a894e361915792f273538e250b1ab7da790544a665c3", - "sha256:1bd7085349dcdf06e52194d0f75ff99fff2eeed0da85a50e4cc2346452c1b8bc" + "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2", + "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.4.2" + "version": "==1.4.3" }, "urlarchiver": { "hashes": [ @@ -1103,11 +1174,11 @@ }, "urllib3": { "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", + "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.10" + "version": "==1.25.11" }, "uwhois": { "editable": true, @@ -1131,12 +1202,12 @@ }, "vulners": { "hashes": [ - "sha256:4e78fc7492d33a1e612e7d5046e51f4c272eb7febdfc0fc06061648d2153e75a", - "sha256:6b088b7c8da9bdcc16e8283afd4a8f804388f1432d12d17b29b770778113ec62", - "sha256:7964884c1f262004a950d5915d49520d22afa3ab175d473492e2dbcc6b5e0a9a" + "sha256:065aa63d5626d51cf45260bc6cc3a6ea682977689c036a6daba695905e881ba7", + "sha256:0e1356040f456f87841ccfe9f2f6ed36a256370606d530679d5d9993fe91386c", + "sha256:ab9ed8fbf1d3c80f0d066b13ac9d70d11dc9cb0b77568be65396117a4245e916" ], "index": "pypi", - "version": "==1.5.8" + "version": "==1.5.9" }, "wand": { "hashes": [ @@ -1169,10 +1240,10 @@ }, "xlsxwriter": { "hashes": [ - "sha256:99b665203d737db31378ec729c9990a004c1abae53a6fa211c104f8c2e36cffd", - "sha256:b89002dea57bb3d4c8207f3e28ef8244bfd9e936b85d74e7dd1f97e11bb70313" + "sha256:9b1ade2d1ba5d9b40a6d1de1d55ded4394ab8002718092ae80a08532c2add2e6", + "sha256:b807c2d3e379bf6a925f472955beef3e07495c1bac708640696876e68675b49b" ], - "version": "==1.3.6" + "version": "==1.3.7" }, "yara-python": { "hashes": [ @@ -1193,26 +1264,42 @@ }, "yarl": { "hashes": [ - "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e", - "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5", - "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580", - "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc", - "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b", - "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2", - "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a", - "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921", - "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e", - "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1", - "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d", - "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131", - "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a", - "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1", - "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188", - "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020", - "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a" + "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843", + "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037", + "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91", + "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805", + "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839", + "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd", + "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6", + "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb", + "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156", + "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6", + "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c", + "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150", + "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c", + "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7", + "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713", + "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019", + "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d", + "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2", + "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6", + "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2", + "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5", + "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb", + "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2", + "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af", + "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c", + "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862", + "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8", + "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a", + "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da", + "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c", + "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385", + "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c", + "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336" ], - "markers": "python_version >= '3.5'", - "version": "==1.6.0" + "markers": "python_version >= '3.6'", + "version": "==1.6.2" } }, "develop": { @@ -1240,12 +1327,12 @@ }, "codecov": { "hashes": [ - "sha256:24545847177a893716b3455ac5bfbafe0465f38d4eb86ea922c09adc7f327e65", - "sha256:355fc7e0c0b8a133045f0d6089bde351c845e7b52b99fec5903b4ea3ab5f6aab", - "sha256:7877f68effde3c2baadcff807a5d13f01019a337f9596eece0d64e57393adf3a" + "sha256:61bc71b5f58be8000bf9235aa9d0112f8fd3acca00aa02191bb81426d22a8584", + "sha256:a333626e6ff882db760ce71a1d84baf80ddff2cd459a3cc49b41fdac47d77ca5", + "sha256:d30ad6084501224b1ba699cbf018a340bb9553eb2701301c14133995fdd84f33" ], "index": "pypi", - "version": "==2.1.9" + "version": "==2.1.10" }, "coverage": { "hashes": [ @@ -1305,10 +1392,10 @@ }, "iniconfig": { "hashes": [ - "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437", - "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69" + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" ], - "version": "==1.0.1" + "version": "==1.1.1" }, "mccabe": { "hashes": [ @@ -1376,11 +1463,11 @@ }, "pytest": { "hashes": [ - "sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9", - "sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92" + "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe", + "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e" ], "index": "pypi", - "version": "==6.1.1" + "version": "==6.1.2" }, "requests": { "extras": [ @@ -1410,11 +1497,11 @@ }, "urllib3": { "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", + "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.10" + "version": "==1.25.11" } } } From bb8c616b6d7d7e1edb00c1b8104ef60967b93a2c Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 29 Oct 2020 18:25:57 +0100 Subject: [PATCH 205/220] fix: Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 354100f..4bfe9ba 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [Greynoise](misp_modules/modules/expansion/greynoise.py) - a hover to get information from greynoise. * [hashdd](misp_modules/modules/expansion/hashdd.py) - a hover module to check file hashes against [hashdd.com](http://www.hashdd.com) including NSLR dataset. * [hibp](misp_modules/modules/expansion/hibp.py) - a hover module to lookup against Have I Been Pwned? -* [html_to_markdown](misp_modules/modules/expansion/html_to_markdown) - Simple HTML to markdown converter +* [html_to_markdown](misp_modules/modules/expansion/html_to_markdown.py) - Simple HTML to markdown converter * [intel471](misp_modules/modules/expansion/intel471.py) - an expansion module to get info from [Intel471](https://intel471.com). * [IPASN](misp_modules/modules/expansion/ipasn.py) - a hover and expansion to get the BGP ASN of an IP address. * [iprep](misp_modules/modules/expansion/iprep.py) - an expansion module to get IP reputation from packetmail.net. From 08d648e2f43c76a301d65d8b1ccf331d9a3e512e Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 29 Oct 2020 18:29:04 +0100 Subject: [PATCH 206/220] fix: [documentation] Updated links to the scripts, with the default branch no longer being master, but main --- doc/README.md | 206 +++++++++++++++++----------------- doc/generate_documentation.py | 2 +- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/doc/README.md b/doc/README.md index dc81b6b..f89a589 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,7 +2,7 @@ ## Expansion Modules -#### [apiosintds](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/apiosintds.py) +#### [apiosintds](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/apiosintds.py) On demand query API for OSINT.digitalside.it project. - **features**: @@ -22,7 +22,7 @@ On demand query API for OSINT.digitalside.it project. ----- -#### [apivoid](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/apivoid.py) +#### [apivoid](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/apivoid.py) @@ -42,7 +42,7 @@ Module to query APIVoid with some domain attributes. ----- -#### [assemblyline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_query.py) +#### [assemblyline_query](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/assemblyline_query.py) @@ -64,7 +64,7 @@ A module tu query the AssemblyLine API with a submission ID to get the submissio ----- -#### [assemblyline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/assemblyline_submit.py) +#### [assemblyline_submit](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/assemblyline_submit.py) @@ -84,7 +84,7 @@ A module to submit samples and URLs to AssemblyLine for advanced analysis, and r ----- -#### [backscatter_io](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/backscatter_io.py) +#### [backscatter_io](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/backscatter_io.py) @@ -104,7 +104,7 @@ Query backscatter.io (https://backscatter.io/). ----- -#### [bgpranking](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/bgpranking.py) +#### [bgpranking](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/bgpranking.py) Query BGP Ranking (https://bgpranking-ng.circl.lu/). - **features**: @@ -122,7 +122,7 @@ Query BGP Ranking (https://bgpranking-ng.circl.lu/). ----- -#### [btc_scam_check](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/btc_scam_check.py) +#### [btc_scam_check](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/btc_scam_check.py) @@ -140,7 +140,7 @@ An expansion hover module to query a special dns blacklist to check if a bitcoin ----- -#### [btc_steroids](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/btc_steroids.py) +#### [btc_steroids](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/btc_steroids.py) @@ -152,7 +152,7 @@ An expansion hover module to get a blockchain balance from a BTC address in MISP ----- -#### [censys_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/censys_enrich.py) +#### [censys_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/censys_enrich.py) An expansion module to enrich attributes in MISP by quering the censys.io API - **features**: @@ -168,7 +168,7 @@ An expansion module to enrich attributes in MISP by quering the censys.io API ----- -#### [circl_passivedns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/circl_passivedns.py) +#### [circl_passivedns](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/circl_passivedns.py) @@ -188,7 +188,7 @@ Module to access CIRCL Passive DNS. ----- -#### [circl_passivessl](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/circl_passivessl.py) +#### [circl_passivessl](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/circl_passivessl.py) @@ -208,7 +208,7 @@ Modules to access CIRCL Passive SSL. ----- -#### [countrycode](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/countrycode.py) +#### [countrycode](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/countrycode.py) Module to expand country codes. - **features**: @@ -222,7 +222,7 @@ Module to expand country codes. ----- -#### [crowdstrike_falcon](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/crowdstrike_falcon.py) +#### [crowdstrike_falcon](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/crowdstrike_falcon.py) @@ -276,7 +276,7 @@ Module to query Crowdstrike Falcon. ----- -#### [cuckoo_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cuckoo_submit.py) +#### [cuckoo_submit](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/cuckoo_submit.py) @@ -295,7 +295,7 @@ An expansion module to submit files and URLs to Cuckoo Sandbox. ----- -#### [cve](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cve.py) +#### [cve](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/cve.py) @@ -311,7 +311,7 @@ An expansion hover module to expand information about CVE id. ----- -#### [cve_advanced](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cve_advanced.py) +#### [cve_advanced](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/cve_advanced.py) @@ -331,7 +331,7 @@ An expansion module to query the CIRCL CVE search API for more information about ----- -#### [cytomic_orion](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cytomic_orion.py) +#### [cytomic_orion](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/cytomic_orion.py) @@ -349,7 +349,7 @@ An expansion module to enrich attributes in MISP by quering the Cytomic Orion AP ----- -#### [dbl_spamhaus](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/dbl_spamhaus.py) +#### [dbl_spamhaus](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/dbl_spamhaus.py) @@ -371,7 +371,7 @@ Module to check Spamhaus DBL for a domain name. ----- -#### [dns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/dns.py) +#### [dns](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/dns.py) A simple DNS expansion service to resolve IP address from domain MISP attributes. - **features**: @@ -389,7 +389,7 @@ A simple DNS expansion service to resolve IP address from domain MISP attributes ----- -#### [docx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/docx_enrich.py) +#### [docx_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/docx_enrich.py) @@ -405,7 +405,7 @@ Module to extract freetext from a .docx document. ----- -#### [domaintools](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/domaintools.py) +#### [domaintools](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/domaintools.py) @@ -442,7 +442,7 @@ DomainTools MISP expansion module. ----- -#### [eql](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/eql.py) +#### [eql](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/eql.py) @@ -458,7 +458,7 @@ EQL query generation for a MISP attribute. ----- -#### [eupi](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/eupi.py) +#### [eupi](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/eupi.py) @@ -478,7 +478,7 @@ A module to query the Phishing Initiative service (https://phishing-initiative.l ----- -#### [farsight_passivedns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/farsight_passivedns.py) +#### [farsight_passivedns](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/farsight_passivedns.py) @@ -496,7 +496,7 @@ Module to access Farsight DNSDB Passive DNS. ----- -#### [geoip_asn](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_asn.py) +#### [geoip_asn](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/geoip_asn.py) - **descrption**: @@ -514,7 +514,7 @@ Module to access Farsight DNSDB Passive DNS. ----- -#### [geoip_city](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_city.py) +#### [geoip_city](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/geoip_city.py) @@ -532,7 +532,7 @@ An expansion module to query a local copy of Maxmind's Geolite database with an ----- -#### [geoip_country](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/geoip_country.py) +#### [geoip_country](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/geoip_country.py) @@ -552,7 +552,7 @@ Module to query a local copy of Maxmind's Geolite database. ----- -#### [google_search](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/google_search.py) +#### [google_search](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/google_search.py) - **descrption**: @@ -570,7 +570,7 @@ Module to query a local copy of Maxmind's Geolite database. ----- -#### [greynoise](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/greynoise.py) +#### [greynoise](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/greynoise.py) @@ -588,7 +588,7 @@ Module to access GreyNoise.io API ----- -#### [hashdd](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/hashdd.py) +#### [hashdd](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/hashdd.py) A hover module to check hashes against hashdd.com including NSLR dataset. - **features**: @@ -602,7 +602,7 @@ A hover module to check hashes against hashdd.com including NSLR dataset. ----- -#### [hibp](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/hibp.py) +#### [hibp](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/hibp.py) @@ -618,7 +618,7 @@ Module to access haveibeenpwned.com API. ----- -#### [html_to_markdown](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/html_to_markdown.py) +#### [html_to_markdown](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/html_to_markdown.py) Expansion module to fetch the html content from an url and convert it into markdown. - **features**: @@ -632,7 +632,7 @@ Expansion module to fetch the html content from an url and convert it into markd ----- -#### [intel471](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/intel471.py) +#### [intel471](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/intel471.py) - **descrption**: @@ -663,7 +663,7 @@ Expansion module to fetch the html content from an url and convert it into markd ----- -#### [intelmq_eventdb](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/intelmq_eventdb.py) +#### [intelmq_eventdb](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/intelmq_eventdb.py) @@ -683,7 +683,7 @@ Module to access intelmqs eventdb. ----- -#### [ipasn](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ipasn.py) +#### [ipasn](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ipasn.py) Module to query an IP ASN history service (https://github.com/D4-project/IPASN-History). - **features**: @@ -699,7 +699,7 @@ Module to query an IP ASN history service (https://github.com/D4-project/IPASN-H ----- -#### [iprep](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/iprep.py) +#### [iprep](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/iprep.py) Module to query IPRep data for IP addresses. - **features**: @@ -715,7 +715,7 @@ Module to query IPRep data for IP addresses. ----- -#### [joesandbox_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) +#### [joesandbox_query](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/joesandbox_query.py) @@ -741,7 +741,7 @@ This url can by the way come from the result of the [joesandbox_submit expansion ----- -#### [joesandbox_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_submit.py) +#### [joesandbox_submit](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/joesandbox_submit.py) @@ -761,7 +761,7 @@ A module to submit files or URLs to Joe Sandbox for an advanced analysis, and re ----- -#### [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) +#### [lastline_query](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/lastline_query.py) @@ -780,7 +780,7 @@ The analysis link can also be retrieved from the output of the [lastline_submit] ----- -#### [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) +#### [lastline_submit](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/lastline_submit.py) @@ -797,7 +797,7 @@ Module to submit a file or URL to Lastline. ----- -#### [macaddress_io](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/macaddress_io.py) +#### [macaddress_io](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/macaddress_io.py) @@ -820,7 +820,7 @@ MISP hover module for macaddress.io ----- -#### [macvendors](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/macvendors.py) +#### [macvendors](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/macvendors.py) @@ -836,7 +836,7 @@ Module to access Macvendors API. ----- -#### [malwarebazaar](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/malwarebazaar.py) +#### [malwarebazaar](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/malwarebazaar.py) Query the MALWAREbazaar API to get additional information about the input hash attribute. - **features**: @@ -852,7 +852,7 @@ Query the MALWAREbazaar API to get additional information about the input hash a ----- -#### [ocr_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ocr_enrich.py) +#### [ocr_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ocr_enrich.py) Module to process some optical character recognition on pictures. - **features**: @@ -866,7 +866,7 @@ Module to process some optical character recognition on pictures. ----- -#### [ods_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ods_enrich.py) +#### [ods_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ods_enrich.py) @@ -882,7 +882,7 @@ Module to extract freetext from a .ods document. ----- -#### [odt_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/odt_enrich.py) +#### [odt_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/odt_enrich.py) @@ -898,7 +898,7 @@ Module to extract freetext from a .odt document. ----- -#### [onyphe](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/onyphe.py) +#### [onyphe](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/onyphe.py) @@ -916,7 +916,7 @@ Module to process a query on Onyphe. ----- -#### [onyphe_full](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/onyphe_full.py) +#### [onyphe_full](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/onyphe_full.py) @@ -936,7 +936,7 @@ Module to process a full query on Onyphe. ----- -#### [otx](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/otx.py) +#### [otx](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/otx.py) @@ -971,7 +971,7 @@ Module to get information from AlienVault OTX. ----- -#### [passivetotal](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/passivetotal.py) +#### [passivetotal](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/passivetotal.py) @@ -1021,7 +1021,7 @@ Module to get information from AlienVault OTX. ----- -#### [pdf_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pdf_enrich.py) +#### [pdf_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/pdf_enrich.py) @@ -1037,7 +1037,7 @@ Module to extract freetext from a PDF document. ----- -#### [pptx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/pptx_enrich.py) +#### [pptx_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/pptx_enrich.py) @@ -1053,7 +1053,7 @@ Module to extract freetext from a .pptx document. ----- -#### [qrcode](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/qrcode.py) +#### [qrcode](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/qrcode.py) Module to decode QR codes. - **features**: @@ -1067,7 +1067,7 @@ Module to decode QR codes. ----- -#### [ransomcoindb](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/ransomcoindb.py) +#### [ransomcoindb](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/ransomcoindb.py) - **descrption**: >Module to access the ransomcoinDB with a hash or btc address attribute and get the associated btc address of hashes. - **features**: @@ -1085,7 +1085,7 @@ Module to decode QR codes. ----- -#### [rbl](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/rbl.py) +#### [rbl](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/rbl.py) Module to check an IPv4 address against known RBLs. - **features**: @@ -1103,7 +1103,7 @@ Module to check an IPv4 address against known RBLs. ----- -#### [recordedfuture](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/recordedfuture.py) +#### [recordedfuture](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/recordedfuture.py) @@ -1121,7 +1121,7 @@ Module to enrich attributes with threat intelligence from Recorded Future. ----- -#### [reversedns](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/reversedns.py) +#### [reversedns](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/reversedns.py) Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes. - **features**: @@ -1139,7 +1139,7 @@ Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes ----- -#### [securitytrails](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/securitytrails.py) +#### [securitytrails](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/securitytrails.py) @@ -1172,7 +1172,7 @@ An expansion modules for SecurityTrails. ----- -#### [shodan](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/shodan.py) +#### [shodan](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/shodan.py) @@ -1190,7 +1190,7 @@ Module to query on Shodan. ----- -#### [sigma_queries](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sigma_queries.py) +#### [sigma_queries](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sigma_queries.py) @@ -1208,7 +1208,7 @@ An expansion hover module to display the result of sigma queries. ----- -#### [sigma_syntax_validator](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sigma_syntax_validator.py) +#### [sigma_syntax_validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sigma_syntax_validator.py) @@ -1228,7 +1228,7 @@ An expansion hover module to perform a syntax check on sigma rules. ----- -#### [sophoslabs_intelix](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sophoslabs_intelix.py) +#### [sophoslabs_intelix](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sophoslabs_intelix.py) @@ -1246,7 +1246,7 @@ An expansion module to query the Sophoslabs intelix API to get additional inform ----- -#### [sourcecache](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/sourcecache.py) +#### [sourcecache](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/sourcecache.py) Module to cache web pages of analysis reports, OSINT sources. The module returns a link of the cached page. - **features**: @@ -1262,7 +1262,7 @@ Module to cache web pages of analysis reports, OSINT sources. The module returns ----- -#### [stix2_pattern_syntax_validator](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) +#### [stix2_pattern_syntax_validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/stix2_pattern_syntax_validator.py) @@ -1282,7 +1282,7 @@ An expansion hover module to perform a syntax check on stix2 patterns. ----- -#### [threatcrowd](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/threatcrowd.py) +#### [threatcrowd](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/threatcrowd.py) @@ -1319,7 +1319,7 @@ Module to get information from ThreatCrowd. ----- -#### [threatminer](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/threatminer.py) +#### [threatminer](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/threatminer.py) @@ -1359,7 +1359,7 @@ Module to get information from ThreatMiner. ----- -#### [trustar_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/trustar_enrich.py) +#### [trustar_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/trustar_enrich.py) @@ -1388,7 +1388,7 @@ Module to get enrich indicators with TruSTAR. ----- -#### [urlhaus](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/urlhaus.py) +#### [urlhaus](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/urlhaus.py) @@ -1406,7 +1406,7 @@ Query of the URLhaus API to get additional information about the input attribute ----- -#### [urlscan](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/urlscan.py) +#### [urlscan](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/urlscan.py) @@ -1426,7 +1426,7 @@ An expansion module to query urlscan.io. ----- -#### [virustotal](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/virustotal.py) +#### [virustotal](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/virustotal.py) @@ -1450,7 +1450,7 @@ Module to get advanced information from virustotal. ----- -#### [virustotal_public](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/virustotal_public.py) +#### [virustotal_public](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/virustotal_public.py) @@ -1474,7 +1474,7 @@ Module to get information from VirusTotal. ----- -#### [vmray_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/vmray_submit.py) +#### [vmray_submit](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vmray_submit.py) @@ -1499,7 +1499,7 @@ Module to submit a sample to VMRay. ----- -#### [vulndb](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/vulndb.py) +#### [vulndb](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vulndb.py) @@ -1519,7 +1519,7 @@ Module to query VulnDB (RiskBasedSecurity.com). ----- -#### [vulners](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/vulners.py) +#### [vulners](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vulners.py) @@ -1539,7 +1539,7 @@ An expansion hover module to expand information about CVE id using Vulners API. ----- -#### [whois](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/whois.py) +#### [whois](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/whois.py) Module to query a local instance of uwhois (https://github.com/rafiot/uwhoisd). - **features**: @@ -1555,7 +1555,7 @@ Module to query a local instance of uwhois (https://github.com/rafiot/uwhoisd). ----- -#### [wiki](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/wiki.py) +#### [wiki](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/wiki.py) @@ -1573,7 +1573,7 @@ An expansion hover module to extract information from Wikidata to have additiona ----- -#### [xforceexchange](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/xforceexchange.py) +#### [xforceexchange](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/xforceexchange.py) @@ -1597,7 +1597,7 @@ An expansion module for IBM X-Force Exchange. ----- -#### [xlsx_enrich](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/xlsx_enrich.py) +#### [xlsx_enrich](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/xlsx_enrich.py) @@ -1613,7 +1613,7 @@ Module to extract freetext from a .xlsx document. ----- -#### [yara_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/yara_query.py) +#### [yara_query](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/yara_query.py) @@ -1632,7 +1632,7 @@ An expansion & hover module to translate any hash attribute into a yara rule. ----- -#### [yara_syntax_validator](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/yara_syntax_validator.py) +#### [yara_syntax_validator](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/yara_syntax_validator.py) @@ -1652,7 +1652,7 @@ An expansion hover module to perform a syntax check on if yara rules are valid o ## Export Modules -#### [cef_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/cef_export.py) +#### [cef_export](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/cef_export.py) Module to export a MISP event in CEF format. - **features**: @@ -1667,7 +1667,7 @@ Module to export a MISP event in CEF format. ----- -#### [cisco_firesight_manager_ACL_rule_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py) +#### [cisco_firesight_manager_ACL_rule_export](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py) @@ -1683,7 +1683,7 @@ Module to export malicious network activity attributes to Cisco fireSIGHT manage ----- -#### [goamlexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/goamlexport.py) +#### [goamlexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/goamlexport.py) @@ -1718,7 +1718,7 @@ This module is used to export MISP events containing transaction objects into Go ----- -#### [liteexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/liteexport.py) +#### [liteexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/liteexport.py) Lite export of a MISP event. - **features**: @@ -1730,7 +1730,7 @@ Lite export of a MISP event. ----- -#### [mass_eql_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/mass_eql_export.py) +#### [mass_eql_export](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/mass_eql_export.py) @@ -1746,7 +1746,7 @@ Mass EQL query export for a MISP event. ----- -#### [nexthinkexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/nexthinkexport.py) +#### [nexthinkexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/nexthinkexport.py) @@ -1762,7 +1762,7 @@ Nexthink NXQL query export module ----- -#### [osqueryexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/osqueryexport.py) +#### [osqueryexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/osqueryexport.py) @@ -1776,7 +1776,7 @@ OSQuery export of a MISP event. ----- -#### [pdfexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/pdfexport.py) +#### [pdfexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/pdfexport.py) Simple export of a MISP event to PDF. - **features**: @@ -1798,13 +1798,13 @@ Simple export of a MISP event to PDF. ----- -#### [testexport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/testexport.py) +#### [testexport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/testexport.py) Skeleton export module. ----- -#### [threatStream_misp_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/threatStream_misp_export.py) +#### [threatStream_misp_export](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/threatStream_misp_export.py) @@ -1822,7 +1822,7 @@ Module to export a structured CSV file for uploading to threatStream. ----- -#### [threat_connect_export](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/threat_connect_export.py) +#### [threat_connect_export](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/threat_connect_export.py) @@ -1841,7 +1841,7 @@ Module to export a structured CSV file for uploading to ThreatConnect. ----- -#### [vt_graph](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/export_mod/vt_graph.py) +#### [vt_graph](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/export_mod/vt_graph.py) @@ -1863,7 +1863,7 @@ This module is used to create a VirusTotal Graph from a MISP event. ## Import Modules -#### [csvimport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/csvimport.py) +#### [csvimport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/csvimport.py) Module to import MISP attributes from a csv file. - **features**: @@ -1883,7 +1883,7 @@ Module to import MISP attributes from a csv file. ----- -#### [cuckooimport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/cuckooimport.py) +#### [cuckooimport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/cuckooimport.py) @@ -1899,7 +1899,7 @@ Module to import Cuckoo JSON. ----- -#### [email_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/email_import.py) +#### [email_import](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/email_import.py) Module to import emails in MISP. - **features**: @@ -1912,7 +1912,7 @@ Module to import emails in MISP. ----- -#### [goamlimport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/goamlimport.py) +#### [goamlimport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/goamlimport.py) @@ -1930,7 +1930,7 @@ Module to import MISP objects about financial transactions from GoAML files. ----- -#### [joe_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/joe_import.py) +#### [joe_import](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/joe_import.py) @@ -1950,7 +1950,7 @@ A module to import data from a Joe Sandbox analysis json report. ----- -#### [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) +#### [lastline_import](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/lastline_import.py) @@ -1968,7 +1968,7 @@ Module to import and parse reports from Lastline analysis links. ----- -#### [mispjson](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/mispjson.py) +#### [mispjson](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/mispjson.py) Module to import MISP JSON format for merging MISP events. - **features**: @@ -1980,7 +1980,7 @@ Module to import MISP JSON format for merging MISP events. ----- -#### [ocr](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/ocr.py) +#### [ocr](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/ocr.py) Optical Character Recognition (OCR) module for MISP. - **features**: @@ -1992,7 +1992,7 @@ Optical Character Recognition (OCR) module for MISP. ----- -#### [openiocimport](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/openiocimport.py) +#### [openiocimport](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/openiocimport.py) Module to import OpenIOC packages. - **features**: @@ -2008,7 +2008,7 @@ Module to import OpenIOC packages. ----- -#### [threatanalyzer_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/threatanalyzer_import.py) +#### [threatanalyzer_import](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/threatanalyzer_import.py) Module to import ThreatAnalyzer archive.zip / analysis.json files. - **features**: @@ -2023,7 +2023,7 @@ Module to import ThreatAnalyzer archive.zip / analysis.json files. ----- -#### [vmray_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/vmray_import.py) +#### [vmray_import](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/import_mod/vmray_import.py) diff --git a/doc/generate_documentation.py b/doc/generate_documentation.py index f86b5a7..e7defa1 100644 --- a/doc/generate_documentation.py +++ b/doc/generate_documentation.py @@ -5,7 +5,7 @@ import json module_types = ['expansion', 'export_mod', 'import_mod'] titles = ['Expansion Modules', 'Export Modules', 'Import Modules'] markdown = ["# MISP modules documentation\n"] -githublink = 'https://github.com/MISP/misp-modules/tree/master/misp_modules/modules' +githublink = 'https://github.com/MISP/misp-modules/tree/main/misp_modules/modules' def generate_doc(root_path): From 260bddb3cf4f8488521ee89cb2414eed48b97e1c Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 2 Nov 2020 19:03:26 +0100 Subject: [PATCH 207/220] chg: [cpe] Changed CVE-Search API default url --- misp_modules/modules/expansion/cpe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 8ea0f65..cd5e5fe 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -12,7 +12,7 @@ moduleinfo = { 'module-type': ['expansion', 'hover'] } moduleconfig = ["custom_API_URL", "limit"] -cveapi_url = 'https://cve.circl.lu/api/cvefor/' +cveapi_url = 'https://cvepremium.circl.lu/api/cvefor/' class VulnerabilitiesParser(): From d9e576e6059cca1ae1a45041ea2cc6308a99f52d Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 3 Nov 2020 19:30:09 +0100 Subject: [PATCH 208/220] chg: [farsight_passivedns] Rework of the module to return MISP objects - All the results are parsed as passive-dns MISP objects - More love to give to the parsing to add references between the passive-dns objects and the input attribute, depending on the type of the query (rrset or rdata), or the rrtype (to be determined) --- .../modules/expansion/farsight_passivedns.py | 133 ++++++++++++------ 1 file changed, 92 insertions(+), 41 deletions(-) diff --git a/misp_modules/modules/expansion/farsight_passivedns.py b/misp_modules/modules/expansion/farsight_passivedns.py index 5d32ea8..30a7e9c 100755 --- a/misp_modules/modules/expansion/farsight_passivedns.py +++ b/misp_modules/modules/expansion/farsight_passivedns.py @@ -1,15 +1,76 @@ import json -from ._dnsdb_query.dnsdb_query import DnsdbClient, QueryError - +from ._dnsdb_query.dnsdb_query import DEFAULT_DNSDB_SERVER, DnsdbClient, QueryError +from . import check_input_attribute, standard_error_message +from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['hostname', 'domain', 'ip-src', 'ip-dst'], 'output': ['freetext']} -moduleinfo = {'version': '0.1', 'author': 'Christophe Vandeplas', 'description': 'Module to access Farsight DNSDB Passive DNS', 'module-type': ['expansion', 'hover']} -moduleconfig = ['apikey'] +mispattributes = { + 'input': ['hostname', 'domain', 'ip-src', 'ip-dst']#, + # 'format': 'misp_standard' +} +moduleinfo = { + 'version': '0.2', + 'author': 'Christophe Vandeplas', + 'description': 'Module to access Farsight DNSDB Passive DNS', + 'module-type': ['expansion', 'hover'] +} +moduleconfig = ['apikey', 'server', 'limit'] -server = 'https://api.dnsdb.info' +DEFAULT_LIMIT = 10 -# TODO return a MISP object with the different attributes + +class FarsightDnsdbParser(): + def __init__(self, attribute): + self.attribute = attribute + self.misp_event = MISPEvent() + self.misp_event.add_attribute(**attribute) + self.passivedns_mapping = { + 'bailiwick': {'type': 'text', 'object_relation': 'bailiwick'}, + 'count': {'type': 'counter', 'object_relation': 'count'}, + 'rdata': {'type': 'text', 'object_relation': 'rdata'}, + 'rrname': {'type': 'text', 'object_relation': 'rrname'}, + 'rrtype': {'type': 'text', 'object_relation': 'rrtype'}, + 'time_first': {'type': 'datetime', 'object_relation': 'time_first'}, + 'time_last': {'type': 'datetime', 'object_relation': 'time_last'}, + 'zone_time_first': {'type': 'datetime', 'object_relation': 'zone_time_first'}, + 'zone_time_last': {'type': 'datetime', 'object_relation': 'zone_time_last'} + } + + def parse_passivedns_results(self, query_response): + default_fields = ('count', 'rrname', 'rrname') + optional_fields = ( + 'bailiwick', + 'time_first', + 'time_last', + 'zone_time_first', + 'zone_time_last' + ) + for result in query_response: + passivedns_object = MISPObject('passive-dns') + for feature in default_fields: + passivedns_object.add_attribute(**self._parse_attribute(feature, result[feature])) + for feature in optional_fields: + if result.get(feature): + passivedns_object.add_attribute(**self._parse_attribute( + feature, + result[feature] + )) + if isinstance(result['rdata'], list): + for rdata in result['rdata']: + passivedns_object.add_attribute(**self._parse_attribute('rdata', rdata)) + else: + passivedns_object.add_attribute(**self._parse_attribute('rdata', result['rdata'])) + self.misp_event.add_object(passivedns_object) + + def get_results(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + def _parse_attribute(self, feature, value): + attribute = {'value': value} + attribute.update(self.passivedns_mapping[feature]) + return attribute def handler(q=False): @@ -19,56 +80,46 @@ def handler(q=False): if not request.get('config') or not request['config'].get('apikey'): misperrors['error'] = 'Farsight DNSDB apikey is missing' return misperrors - client = DnsdbClient(server, request['config']['apikey']) - if request.get('hostname'): - res = lookup_name(client, request['hostname']) - elif request.get('domain'): - res = lookup_name(client, request['domain']) - elif request.get('ip-src'): - res = lookup_ip(client, request['ip-src']) - elif request.get('ip-dst'): - res = lookup_ip(client, request['ip-dst']) - else: - misperrors['error'] = "Unsupported attributes type" - return misperrors - - out = '' - for v in set(res): # uniquify entries - out = out + "{} ".format(v) - r = {'results': [{'types': mispattributes['output'], 'values': out}]} - return r + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] + if attribute['type'] not in mispattributes['input']: + return {'error': 'Unsupported attributes type'} + config = request['config'] + args = {'apikey': config['apikey']} + for feature, default in zip(('server', 'limit'), (DEFAULT_DNSDB_SERVER, DEFAULT_LIMIT)): + args[feature] = config[feature] if config.get(feature) else default + client = DnsdbClient(**args) + to_query = lookup_ip if attribute['type'] in ('ip-src', 'ip-dst') else lookup_name + response = to_query(client, attribute['value']) + if not response: + return {'error': f"Empty results on Farsight DNSDB for the queries {attribute['type']}: {attribute['value']}."} + parser = FarsightDnsdbParser(attribute) + parser.parse_passivedns_results(response) + return parser.get_results() def lookup_name(client, name): try: res = client.query_rrset(name) # RRSET = entries in the left-hand side of the domain name related labels - for item in res: - if item.get('rrtype') in ['A', 'AAAA', 'CNAME']: - for i in item.get('rdata'): - yield(i.rstrip('.')) - if item.get('rrtype') in ['SOA']: - for i in item.get('rdata'): - # grab email field and replace first dot by @ to convert to an email address - yield(i.split(' ')[1].rstrip('.').replace('.', '@', 1)) + response = list(res) except QueryError: - pass - + response = [] try: res = client.query_rdata_name(name) # RDATA = entries on the right-hand side of the domain name related labels - for item in res: - if item.get('rrtype') in ['A', 'AAAA', 'CNAME']: - yield(item.get('rrname').rstrip('.')) + response.extend(list(res)) except QueryError: pass + return response def lookup_ip(client, ip): try: res = client.query_rdata_ip(ip) - for item in res: - yield(item['rrname'].rstrip('.')) + response = list(res) except QueryError: - pass + response = [] + return response def introspection(): From 7c5465e02bec3ebd311c24c3aeb3583a28191ec7 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 4 Nov 2020 18:36:06 +0100 Subject: [PATCH 209/220] fix: [dnsdb] Avoiding AttributeError with the sys library, probably depending on the python version --- misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py b/misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py index af3f204..5df1207 100755 --- a/misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py +++ b/misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py @@ -119,7 +119,10 @@ class DnsdbClient(object): break yield json.loads(line.decode('ascii')) except (HTTPError, URLError) as e: - raise QueryError(str(e), sys.exc_traceback) + try: + raise QueryError(str(e), sys.exc_traceback) + except AttributeError: + raise QueryError(str(e), sys.exc_info) def quote(path): From c0440a0d3304a3a945709175987886f3cbe98930 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 4 Nov 2020 18:37:57 +0100 Subject: [PATCH 210/220] chg: [farsight_passivedns] More context added to the results - References between the passive-dns objects and the initial attribute - Comment on object attributes mentioning whether the results come from an rrset or an rdata lookup --- .../modules/expansion/farsight_passivedns.py | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/misp_modules/modules/expansion/farsight_passivedns.py b/misp_modules/modules/expansion/farsight_passivedns.py index 30a7e9c..7dd47a5 100755 --- a/misp_modules/modules/expansion/farsight_passivedns.py +++ b/misp_modules/modules/expansion/farsight_passivedns.py @@ -35,6 +35,13 @@ class FarsightDnsdbParser(): 'zone_time_first': {'type': 'datetime', 'object_relation': 'zone_time_first'}, 'zone_time_last': {'type': 'datetime', 'object_relation': 'zone_time_last'} } + self.type_to_feature = { + 'domain': 'domain name', + 'hostname': 'hostname', + 'ip-src': 'IP address', + 'ip-dst': 'IP address' + } + self.comment = 'Result from an %s lookup on DNSDB about the %s: %s' def parse_passivedns_results(self, query_response): default_fields = ('count', 'rrname', 'rrname') @@ -45,30 +52,30 @@ class FarsightDnsdbParser(): 'zone_time_first', 'zone_time_last' ) - for result in query_response: - passivedns_object = MISPObject('passive-dns') - for feature in default_fields: - passivedns_object.add_attribute(**self._parse_attribute(feature, result[feature])) - for feature in optional_fields: - if result.get(feature): - passivedns_object.add_attribute(**self._parse_attribute( - feature, - result[feature] - )) - if isinstance(result['rdata'], list): - for rdata in result['rdata']: - passivedns_object.add_attribute(**self._parse_attribute('rdata', rdata)) - else: - passivedns_object.add_attribute(**self._parse_attribute('rdata', result['rdata'])) - self.misp_event.add_object(passivedns_object) + for query_type, results in query_response.items(): + comment = self.comment % (query_type, self.type_to_feature[self.attribute['type']], self.attribute['value']) + for result in results: + passivedns_object = MISPObject('passive-dns') + for feature in default_fields: + passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature])) + for feature in optional_fields: + if result.get(feature): + passivedns_object.add_attribute(**self._parse_attribute(comment, feature, result[feature])) + if isinstance(result['rdata'], list): + for rdata in result['rdata']: + passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', rdata)) + else: + passivedns_object.add_attribute(**self._parse_attribute(comment, 'rdata', result['rdata'])) + passivedns_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(passivedns_object) def get_results(self): event = json.loads(self.misp_event.to_json()) results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} - def _parse_attribute(self, feature, value): - attribute = {'value': value} + def _parse_attribute(self, comment, feature, value): + attribute = {'value': value, 'comment': comment} attribute.update(self.passivedns_mapping[feature]) return attribute @@ -100,14 +107,15 @@ def handler(q=False): def lookup_name(client, name): + response = {} try: res = client.query_rrset(name) # RRSET = entries in the left-hand side of the domain name related labels - response = list(res) + response['rrset'] = list(res) except QueryError: - response = [] + pass try: res = client.query_rdata_name(name) # RDATA = entries on the right-hand side of the domain name related labels - response.extend(list(res)) + response['rdata'] = list(res) except QueryError: pass return response @@ -116,9 +124,9 @@ def lookup_name(client, name): def lookup_ip(client, ip): try: res = client.query_rdata_ip(ip) - response = list(res) + response = {'rdata': list(res)} except QueryError: - response = [] + response = {} return response From a357243d314ca3cb7a288e3e0ad441da7b793f62 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 5 Nov 2020 13:06:08 +0100 Subject: [PATCH 211/220] chg: [doc] Updated the farsight_passivedns module documentation --- doc/README.md | 11 ++++++++--- doc/expansion/farsight_passivedns.json | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/README.md b/doc/README.md index f2acc42..3b2bceb 100644 --- a/doc/README.md +++ b/doc/README.md @@ -505,13 +505,18 @@ A module to query the Phishing Initiative service (https://phishing-initiative.l Module to access Farsight DNSDB Passive DNS. - **features**: ->This module takes a domain, hostname or IP address MISP attribute as input to query the Farsight Passive DNS API. The API returns then the result of the query with some information about the value queried. +>This module takes a domain, hostname or IP address MISP attribute as input to query the Farsight Passive DNS API. +> The results of rdata and rrset lookups are then returned and parsed into passive-dns objects. +> +>An API key is required to submit queries to the API. +> It is also possible to define a custom server URL, and to set a limit of results to get. +> This limit is set for each lookup, which means we can have an up to the limit number of passive-dns objects resulting from an rdata query about an IP address, but an up to the limit number of passive-dns objects for each lookup queries about a domain or a hostname (== twice the limit). - **input**: >A domain, hostname or IP address MISP attribute. - **output**: ->Text containing information about the input, resulting from the query on the Farsight Passive DNS API. +>Passive-dns objects, resulting from the query on the Farsight Passive DNS API. - **references**: ->https://www.farsightsecurity.com/ +>https://www.farsightsecurity.com/, https://docs.dnsdb.info/dnsdb-api/ - **requirements**: >An access to the Farsight Passive DNS API (apikey) diff --git a/doc/expansion/farsight_passivedns.json b/doc/expansion/farsight_passivedns.json index 2c1bf05..2dbc64e 100644 --- a/doc/expansion/farsight_passivedns.json +++ b/doc/expansion/farsight_passivedns.json @@ -3,7 +3,7 @@ "logo": "logos/farsight.png", "requirements": ["An access to the Farsight Passive DNS API (apikey)"], "input": "A domain, hostname or IP address MISP attribute.", - "output": "Text containing information about the input, resulting from the query on the Farsight Passive DNS API.", - "references": ["https://www.farsightsecurity.com/"], - "features": "This module takes a domain, hostname or IP address MISP attribute as input to query the Farsight Passive DNS API. The API returns then the result of the query with some information about the value queried." + "output": "Passive-dns objects, resulting from the query on the Farsight Passive DNS API.", + "references": ["https://www.farsightsecurity.com/", "https://docs.dnsdb.info/dnsdb-api/"], + "features": "This module takes a domain, hostname or IP address MISP attribute as input to query the Farsight Passive DNS API.\n The results of rdata and rrset lookups are then returned and parsed into passive-dns objects.\n\nAn API key is required to submit queries to the API.\n It is also possible to define a custom server URL, and to set a limit of results to get.\n This limit is set for each lookup, which means we can have an up to the limit number of passive-dns objects resulting from an rdata query about an IP address, but an up to the limit number of passive-dns objects for each lookup queries about a domain or a hostname (== twice the limit)." } From 87db6f04aa8c443bfae9dc68e521c836af4f119d Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 5 Nov 2020 15:56:01 +0100 Subject: [PATCH 212/220] fix: [tests] Small fixes on the expansion tests --- 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 1aa0f7a..eb29332 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -221,7 +221,7 @@ class TestExpansions(unittest.TestCase): try: self.assertIn(result, self.get_values(response)) except Exception: - self.assertTrue(self.get_errors(response).startwith('Something went wrong')) + self.assertTrue(self.get_errors(response).startswith('Something went wrong')) else: query = {"module": module_name, "ip-src": "8.8.8.8"} response = self.misp_modules_post(query) @@ -285,7 +285,7 @@ class TestExpansions(unittest.TestCase): 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') + self.assertEqual(self.get_values(response).strip('\n'), 'Threat Sharing') def test_ods(self): filename = 'test.ods' From d9cfcf8f62fcf849557c4864d04596809ed792de Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Thu, 5 Nov 2020 17:51:41 +0100 Subject: [PATCH 213/220] fix: [farsight_passivedns] Uncommented mandatory field that was commented for tests --- misp_modules/modules/expansion/farsight_passivedns.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/farsight_passivedns.py b/misp_modules/modules/expansion/farsight_passivedns.py index 7dd47a5..a338bfb 100755 --- a/misp_modules/modules/expansion/farsight_passivedns.py +++ b/misp_modules/modules/expansion/farsight_passivedns.py @@ -5,8 +5,8 @@ from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = { - 'input': ['hostname', 'domain', 'ip-src', 'ip-dst']#, - # 'format': 'misp_standard' + 'input': ['hostname', 'domain', 'ip-src', 'ip-dst'], + 'format': 'misp_standard' } moduleinfo = { 'version': '0.2', From b98562a75eef61aabcc5aa84a00de21682bdc878 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 10 Nov 2020 17:53:47 +0100 Subject: [PATCH 214/220] chg: [cpe] Support of the new CVE-Search API --- misp_modules/modules/expansion/cpe.py | 28 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index cd5e5fe..bf6f7b6 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -6,19 +6,18 @@ from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} mispattributes = {'input': ['cpe'], 'format': 'misp_standard'} moduleinfo = { - 'version': '1', + 'version': '2', 'author': 'Christian Studer', 'description': 'An expansion module to enrich a CPE attribute with its related vulnerabilities.', 'module-type': ['expansion', 'hover'] } moduleconfig = ["custom_API_URL", "limit"] -cveapi_url = 'https://cvepremium.circl.lu/api/cvefor/' +cveapi_url = 'https://cvepremium.circl.lu/api/query' class VulnerabilitiesParser(): - def __init__(self, attribute, api_url): + def __init__(self, attribute): self.attribute = attribute - self.api_url = api_url self.misp_event = MISPEvent() self.misp_event.add_attribute(**attribute) self.vulnerability_mapping = { @@ -100,18 +99,27 @@ def handler(q=False): attribute = request['attribute'] if attribute.get('type') != 'cpe': return {'error': 'Wrong input attribute type.'} - api_url = check_url(request['config']['custom_API_URL']) if request['config'].get('custom_API_URL') else cveapi_url - url = f"{api_url}{attribute['value']}" + url = check_url(request['config']['custom_API_URL']) if request['config'].get('custom_API_URL') else cveapi_url + params = { + "retrieve": "cves", + "dict_filter": { + "vulnerable_configuration": attribute['value'] + } + } if request['config'].get('limit'): - url = f"{url}/{request['config']['limit']}" - response = requests.get(url) + params.update({ + "limit": int(request['config']['limit']), + "sort": "cvss", + "sort_dir": "DESC" + }) + response = requests.post(url, json=params) if response.status_code == 200: - vulnerabilities = response.json() + vulnerabilities = response.json()['data'] if not vulnerabilities: return {'error': 'No related vulnerability for this CPE.'} else: return {'error': 'API not accessible.'} - parser = VulnerabilitiesParser(attribute, api_url) + parser = VulnerabilitiesParser(attribute) parser.parse_vulnerabilities(vulnerabilities) return parser.get_result() From 0650126d6a8117efb03ff9ae2c3f21d1911a442d Mon Sep 17 00:00:00 2001 From: Jesse Hedden Date: Tue, 10 Nov 2020 17:20:03 -0800 Subject: [PATCH 215/220] fixed typo causing firstSeen and lastSeen to not be pulled from enrichment data --- misp_modules/modules/expansion/trustar_enrich.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/trustar_enrich.py b/misp_modules/modules/expansion/trustar_enrich.py index ab472af..1724441 100644 --- a/misp_modules/modules/expansion/trustar_enrich.py +++ b/misp_modules/modules/expansion/trustar_enrich.py @@ -39,7 +39,7 @@ class TruSTARParser: # Relevant fields from each TruSTAR endpoint SUMMARY_FIELDS = ["severityLevel", "source", "score", "attributes"] - METADATA_FIELDS = ["sightings", "first_seen", "last_seen", "tags"] + METADATA_FIELDS = ["sightings", "firstSeen", "lastSeen", "tags"] REPORT_BASE_URL = "https://station.trustar.co/constellation/reports/{}" From bd3fa3ea07d73b60519c5df2b85e7360518cc413 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 13 Nov 2020 15:46:41 +0100 Subject: [PATCH 216/220] chg: [cpe] Added default limit to the results - Results returned by CVE-search are sorted by cvss score and limited in number to avoid potential massive amount of data retuned back to MISP. - Users can overwrite the default limit with the configuration already present as optional, and can also set the limit to 0 to get the full list of results --- misp_modules/modules/expansion/cpe.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index bf6f7b6..83cbc46 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -13,6 +13,7 @@ moduleinfo = { } moduleconfig = ["custom_API_URL", "limit"] cveapi_url = 'https://cvepremium.circl.lu/api/query' +DEFAULT_LIMIT = 10 class VulnerabilitiesParser(): @@ -99,19 +100,18 @@ def handler(q=False): attribute = request['attribute'] if attribute.get('type') != 'cpe': return {'error': 'Wrong input attribute type.'} - url = check_url(request['config']['custom_API_URL']) if request['config'].get('custom_API_URL') else cveapi_url + config = request['config'] + url = check_url(config['custom_API_URL']) if config.get('custom_API_URL') else cveapi_url + limit = int(config['limit']) if config.get('limit') else DEFAULT_LIMIT params = { "retrieve": "cves", "dict_filter": { "vulnerable_configuration": attribute['value'] - } + }, + "limit": limit, + "sort": "cvss", + "sort_dir": "DESC" } - if request['config'].get('limit'): - params.update({ - "limit": int(request['config']['limit']), - "sort": "cvss", - "sort_dir": "DESC" - }) response = requests.post(url, json=params) if response.status_code == 200: vulnerabilities = response.json()['data'] From 32c0bf9ae28c3b0a5df16b458d5a0f6dd983491f Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 13 Nov 2020 15:49:58 +0100 Subject: [PATCH 217/220] fix: [cpe] Fixed typo in vulnerable-configuration object relation fields --- misp_modules/modules/expansion/cpe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 83cbc46..600ff37 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -32,11 +32,11 @@ class VulnerabilitiesParser(): }, 'vulnerable_configuration': { 'type': 'cpe', - 'object_relation': 'vulnerable_configuration' + 'object_relation': 'vulnerable-configuration' }, 'vulnerable_configuration_cpe_2_2': { 'type': 'cpe', - 'object_relation': 'vulnerable_configuration' + 'object_relation': 'vulnerable-configuration' }, 'Modified': { 'type': 'datetime', From a36bd20f31f67114464546d56aae87272276bcd7 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sun, 15 Nov 2020 19:27:21 +0100 Subject: [PATCH 218/220] fix: [documentation] Added missing cpe module documentation --- doc/expansion/cpe.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/expansion/cpe.json diff --git a/doc/expansion/cpe.json b/doc/expansion/cpe.json new file mode 100644 index 0000000..5927e5a --- /dev/null +++ b/doc/expansion/cpe.json @@ -0,0 +1,8 @@ +{ + "description": "An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities.", + "logo": "logos/cpe.py", + "input": "CPE attribute.", + "output": "The vulnerabilities related to the CPE.", + "references": ["https://cve.circl.lu/api/"], + "features": "The module takes a cpe attribute as input and queries the CVE search API to get its related vulnerabilities. \nThe list of vulnerabilities is then parsed and returned as vulnerability objects.\n\nUsers can use their own CVE search API url by defining a value to the custom_API_URL parameter. If no custom API url is given, the default cve.circl.lu api url is used.\n\nIn order to limit the amount of data returned by CVE serach, users can also the limit parameter. With the limit set, the API returns only the requested number of vulnerabilities, sorted from the highest cvss score to the lowest one." +} From b29658dee61874cf9e30f5087d7f7e9ad52db273 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 17 Nov 2020 19:36:19 +0100 Subject: [PATCH 219/220] chg: [documentation] Cleaner documentation directories & auto-generation Including: - A move of the previous `doc` and `docs` directories to `documentation` - `documentation` is now the default directory - The documentation previously under `doc` is now in `documentation/website` - The mkdocs previously under `docs` is now in `documentation/mkdocs` - All single JSON documentation files have been JQed - Some small improvements to list fields displaying --- doc/expansion/greynoise.json | 9 -- doc/export_mod/cef_export.json | 8 -- doc/export_mod/goamlexport.json | 9 -- doc/export_mod/liteexport.json | 8 -- doc/export_mod/nexthinkexport.json | 9 -- doc/export_mod/osqueryexport.json | 9 -- doc/export_mod/pdfexport.json | 8 -- doc/export_mod/threatStream_misp_export.json | 9 -- doc/export_mod/threat_connect_export.json | 9 -- doc/generate_documentation.py | 65 --------- doc/import_mod/csvimport.json | 8 -- doc/import_mod/cuckooimport.json | 9 -- doc/import_mod/email_import.json | 8 -- doc/import_mod/goamlimport.json | 9 -- doc/import_mod/mispjson.json | 8 -- doc/import_mod/ocr.json | 8 -- doc/import_mod/openiocimport.json | 8 -- doc/import_mod/threatanalyzer_import.json | 8 -- doc/import_mod/vmray_import.json | 9 -- {doc => documentation}/README.md | 124 +++++++++++------- documentation/generate_documentation.py | 68 ++++++++++ {doc => documentation}/logos/apivoid.png | Bin {doc => documentation}/logos/assemblyline.png | Bin .../logos/backscatter_io.png | Bin {doc => documentation}/logos/bitcoin.png | Bin {doc => documentation}/logos/cisco.png | Bin {doc => documentation}/logos/crowdstrike.png | Bin {doc => documentation}/logos/cuckoo.png | Bin {doc => documentation}/logos/cve.png | Bin .../logos/cytomic_orion.png | Bin {doc => documentation}/logos/docx.png | Bin {doc => documentation}/logos/domaintools.png | Bin {doc => documentation}/logos/eql.png | Bin {doc => documentation}/logos/eupi.png | Bin {doc => documentation}/logos/farsight.png | Bin {doc => documentation}/logos/goAML.jpg | Bin {doc => documentation}/logos/google.png | Bin {doc => documentation}/logos/greynoise.png | Bin {doc => documentation}/logos/hibp.png | Bin {doc => documentation}/logos/intel471.png | Bin {doc => documentation}/logos/intelmq.png | Bin {doc => documentation}/logos/joesandbox.png | Bin {doc => documentation}/logos/lastline.png | Bin .../logos/macaddress_io.png | Bin {doc => documentation}/logos/macvendors.png | Bin {doc => documentation}/logos/maxmind.png | Bin {doc => documentation}/logos/nexthink.svg | 0 {doc => documentation}/logos/ods.png | Bin {doc => documentation}/logos/odt.png | Bin {doc => documentation}/logos/onyphe.jpg | Bin {doc => documentation}/logos/osquery.png | Bin {doc => documentation}/logos/otx.png | Bin {doc => documentation}/logos/passivedns.png | Bin {doc => documentation}/logos/passivessl.png | Bin {doc => documentation}/logos/passivetotal.png | Bin {doc => documentation}/logos/pdf.jpg | Bin {doc => documentation}/logos/pptx.png | Bin .../logos/recordedfuture.png | Bin .../logos/securitytrails.png | Bin {doc => documentation}/logos/shodan.png | Bin {doc => documentation}/logos/sigma.png | Bin .../logos/sophoslabs_intelix.svg | 0 {doc => documentation}/logos/spamhaus.jpg | Bin {doc => documentation}/logos/stix.png | Bin .../logos/threatconnect.png | Bin {doc => documentation}/logos/threatcrowd.png | Bin {doc => documentation}/logos/threatminer.png | Bin {doc => documentation}/logos/threatstream.png | Bin {doc => documentation}/logos/trustar.png | Bin {doc => documentation}/logos/urlhaus.png | Bin {doc => documentation}/logos/urlscan.jpg | Bin {doc => documentation}/logos/virustotal.png | Bin {doc => documentation}/logos/vmray.png | Bin {doc => documentation}/logos/vulndb.png | Bin {doc => documentation}/logos/vulners.png | Bin {doc => documentation}/logos/wikidata.png | Bin {doc => documentation}/logos/xforce.png | Bin {doc => documentation}/logos/xlsx.png | Bin {doc => documentation}/logos/yara.png | Bin .../mkdocs}/REQUIREMENTS.txt | 0 {docs => documentation/mkdocs}/contribute.md | 0 .../mkdocs}/img/favicon.ico | Bin {docs => documentation/mkdocs}/img/misp.png | Bin {docs => documentation/mkdocs}/index.md | 0 {docs => documentation/mkdocs}/install.md | 0 {docs => documentation/mkdocs}/license.md | 0 .../website}/expansion/apiosintds.json | 10 +- .../website}/expansion/apivoid.json | 12 +- .../expansion/assemblyline_query.json | 12 +- .../expansion/assemblyline_submit.json | 12 +- .../website}/expansion/backscatter_io.json | 12 +- .../website}/expansion/bgpranking.json | 10 +- .../website}/expansion/btc_scam_check.json | 14 +- .../website}/expansion/btc_steroids.json | 4 +- .../website}/expansion/censys_enrich.json | 10 +- .../website}/expansion/circl_passivedns.json | 14 +- .../website}/expansion/circl_passivessl.json | 13 +- .../website}/expansion/countrycode.json | 2 +- .../website}/expansion/cpe.json | 8 +- .../expansion/crowdstrike_falcon.json | 12 +- .../website}/expansion/cuckoo_submit.json | 13 +- .../website}/expansion/cve.json | 9 +- .../website}/expansion/cve_advanced.json | 9 +- .../website}/expansion/cytomic_orion.json | 13 +- .../website}/expansion/dbl_spamhaus.json | 12 +- .../website}/expansion/dns.json | 6 +- .../website}/expansion/docx_enrich.json | 8 +- .../website}/expansion/domaintools.json | 13 +- .../website}/expansion/eql.json | 8 +- .../website}/expansion/eupi.json | 13 +- .../expansion/farsight_passivedns.json | 13 +- .../website}/expansion/geoip_asn.json | 12 +- .../website}/expansion/geoip_city.json | 12 +- .../website}/expansion/geoip_country.json | 12 +- .../website}/expansion/google_search.json | 12 +- .../website/expansion/greynoise.json | 14 ++ .../website}/expansion/hashdd.json | 6 +- .../website}/expansion/hibp.json | 8 +- .../website}/expansion/html_to_markdown.json | 6 +- .../website}/expansion/intel471.json | 12 +- .../website}/expansion/intelmq_eventdb.json | 14 +- .../website}/expansion/ipasn.json | 10 +- .../website}/expansion/iprep.json | 10 +- .../website}/expansion/joesandbox_query.json | 13 +- .../website}/expansion/joesandbox_submit.json | 13 +- .../website}/expansion/lastline_query.json | 8 +- .../website}/expansion/lastline_submit.json | 8 +- .../website}/expansion/macaddress_io.json | 14 +- .../website}/expansion/macvendors.json | 9 +- .../website}/expansion/malwarebazaar.json | 6 +- .../website}/expansion/ocr_enrich.json | 6 +- .../website}/expansion/ods_enrich.json | 10 +- .../website}/expansion/odt_enrich.json | 8 +- .../website}/expansion/onyphe.json | 14 +- .../website}/expansion/onyphe_full.json | 14 +- .../website}/expansion/otx.json | 12 +- .../website}/expansion/passivetotal.json | 13 +- .../website}/expansion/pdf_enrich.json | 8 +- .../website}/expansion/pptx_enrich.json | 8 +- .../website}/expansion/qrcode.json | 8 +- .../website}/expansion/ransomcoindb.json | 10 +- .../website}/expansion/rbl.json | 10 +- .../website}/expansion/recordedfuture.json | 12 +- .../website}/expansion/reversedns.json | 6 +- .../website}/expansion/securitytrails.json | 13 +- .../website}/expansion/shodan.json | 13 +- .../website}/expansion/sigma_queries.json | 12 +- .../expansion/sigma_syntax_validator.json | 13 +- .../expansion/sophoslabs_intelix.json | 12 +- .../website}/expansion/sourcecache.json | 10 +- .../stix2_pattern_syntax_validator.json | 12 +- .../website}/expansion/threatcrowd.json | 8 +- .../website}/expansion/threatminer.json | 8 +- .../website}/expansion/trustar_enrich.json | 8 +- .../website}/expansion/urlhaus.json | 8 +- .../website}/expansion/urlscan.json | 12 +- .../website}/expansion/virustotal.json | 13 +- .../website}/expansion/virustotal_public.json | 13 +- .../website}/expansion/vmray_submit.json | 12 +- .../website}/expansion/vulndb.json | 12 +- .../website}/expansion/vulners.json | 13 +- .../website}/expansion/whois.json | 10 +- .../website}/expansion/wiki.json | 12 +- .../website}/expansion/xforceexchange.json | 12 +- .../website}/expansion/xlsx_enrich.json | 8 +- .../website}/expansion/yara_query.json | 13 +- .../expansion/yara_syntax_validator.json | 12 +- .../website/export_mod/cef_export.json | 10 ++ ...sco_firesight_manager_ACL_rule_export.json | 8 +- .../website/export_mod/goamlexport.json | 14 ++ .../website/export_mod/liteexport.json | 8 ++ .../website}/export_mod/mass_eql_export.json | 8 +- .../website/export_mod/nexthinkexport.json | 11 ++ .../website/export_mod/osqueryexport.json | 9 ++ .../website/export_mod/pdfexport.json | 13 ++ .../website}/export_mod/testexport.json | 2 +- .../export_mod/threatStream_misp_export.json | 14 ++ .../export_mod/threat_connect_export.json | 13 ++ .../website}/export_mod/vt_graph.json | 12 +- .../website/import_mod/csvimport.json | 13 ++ .../website/import_mod/cuckooimport.json | 12 ++ .../website/import_mod/email_import.json | 8 ++ .../website/import_mod/goamlimport.json | 11 ++ .../website}/import_mod/joe_import.json | 9 +- .../website}/import_mod/lastline_import.json | 8 +- .../website/import_mod/mispjson.json | 8 ++ documentation/website/import_mod/ocr.json | 8 ++ .../website/import_mod/openiocimport.json | 12 ++ .../import_mod/threatanalyzer_import.json | 10 ++ .../website/import_mod/vmray_import.json | 13 ++ 190 files changed, 945 insertions(+), 558 deletions(-) delete mode 100644 doc/expansion/greynoise.json delete mode 100644 doc/export_mod/cef_export.json delete mode 100644 doc/export_mod/goamlexport.json delete mode 100644 doc/export_mod/liteexport.json delete mode 100644 doc/export_mod/nexthinkexport.json delete mode 100644 doc/export_mod/osqueryexport.json delete mode 100644 doc/export_mod/pdfexport.json delete mode 100644 doc/export_mod/threatStream_misp_export.json delete mode 100644 doc/export_mod/threat_connect_export.json delete mode 100644 doc/generate_documentation.py delete mode 100644 doc/import_mod/csvimport.json delete mode 100644 doc/import_mod/cuckooimport.json delete mode 100644 doc/import_mod/email_import.json delete mode 100644 doc/import_mod/goamlimport.json delete mode 100644 doc/import_mod/mispjson.json delete mode 100644 doc/import_mod/ocr.json delete mode 100644 doc/import_mod/openiocimport.json delete mode 100644 doc/import_mod/threatanalyzer_import.json delete mode 100644 doc/import_mod/vmray_import.json rename {doc => documentation}/README.md (96%) create mode 100644 documentation/generate_documentation.py rename {doc => documentation}/logos/apivoid.png (100%) rename {doc => documentation}/logos/assemblyline.png (100%) rename {doc => documentation}/logos/backscatter_io.png (100%) rename {doc => documentation}/logos/bitcoin.png (100%) rename {doc => documentation}/logos/cisco.png (100%) rename {doc => documentation}/logos/crowdstrike.png (100%) rename {doc => documentation}/logos/cuckoo.png (100%) rename {doc => documentation}/logos/cve.png (100%) rename {doc => documentation}/logos/cytomic_orion.png (100%) rename {doc => documentation}/logos/docx.png (100%) rename {doc => documentation}/logos/domaintools.png (100%) rename {doc => documentation}/logos/eql.png (100%) rename {doc => documentation}/logos/eupi.png (100%) rename {doc => documentation}/logos/farsight.png (100%) rename {doc => documentation}/logos/goAML.jpg (100%) rename {doc => documentation}/logos/google.png (100%) rename {doc => documentation}/logos/greynoise.png (100%) rename {doc => documentation}/logos/hibp.png (100%) rename {doc => documentation}/logos/intel471.png (100%) rename {doc => documentation}/logos/intelmq.png (100%) rename {doc => documentation}/logos/joesandbox.png (100%) rename {doc => documentation}/logos/lastline.png (100%) rename {doc => documentation}/logos/macaddress_io.png (100%) rename {doc => documentation}/logos/macvendors.png (100%) rename {doc => documentation}/logos/maxmind.png (100%) rename {doc => documentation}/logos/nexthink.svg (100%) rename {doc => documentation}/logos/ods.png (100%) rename {doc => documentation}/logos/odt.png (100%) rename {doc => documentation}/logos/onyphe.jpg (100%) rename {doc => documentation}/logos/osquery.png (100%) rename {doc => documentation}/logos/otx.png (100%) rename {doc => documentation}/logos/passivedns.png (100%) rename {doc => documentation}/logos/passivessl.png (100%) rename {doc => documentation}/logos/passivetotal.png (100%) rename {doc => documentation}/logos/pdf.jpg (100%) rename {doc => documentation}/logos/pptx.png (100%) rename {doc => documentation}/logos/recordedfuture.png (100%) rename {doc => documentation}/logos/securitytrails.png (100%) rename {doc => documentation}/logos/shodan.png (100%) rename {doc => documentation}/logos/sigma.png (100%) rename {doc => documentation}/logos/sophoslabs_intelix.svg (100%) rename {doc => documentation}/logos/spamhaus.jpg (100%) rename {doc => documentation}/logos/stix.png (100%) rename {doc => documentation}/logos/threatconnect.png (100%) rename {doc => documentation}/logos/threatcrowd.png (100%) rename {doc => documentation}/logos/threatminer.png (100%) rename {doc => documentation}/logos/threatstream.png (100%) rename {doc => documentation}/logos/trustar.png (100%) rename {doc => documentation}/logos/urlhaus.png (100%) rename {doc => documentation}/logos/urlscan.jpg (100%) rename {doc => documentation}/logos/virustotal.png (100%) rename {doc => documentation}/logos/vmray.png (100%) rename {doc => documentation}/logos/vulndb.png (100%) rename {doc => documentation}/logos/vulners.png (100%) rename {doc => documentation}/logos/wikidata.png (100%) rename {doc => documentation}/logos/xforce.png (100%) rename {doc => documentation}/logos/xlsx.png (100%) rename {doc => documentation}/logos/yara.png (100%) rename {docs => documentation/mkdocs}/REQUIREMENTS.txt (100%) rename {docs => documentation/mkdocs}/contribute.md (100%) rename {docs => documentation/mkdocs}/img/favicon.ico (100%) rename {docs => documentation/mkdocs}/img/misp.png (100%) rename {docs => documentation/mkdocs}/index.md (100%) rename {docs => documentation/mkdocs}/install.md (100%) rename {docs => documentation/mkdocs}/license.md (100%) rename {doc => documentation/website}/expansion/apiosintds.json (76%) rename {doc => documentation/website}/expansion/apivoid.json (72%) rename {doc => documentation/website}/expansion/assemblyline_query.json (78%) rename {doc => documentation/website}/expansion/assemblyline_submit.json (72%) rename {doc => documentation/website}/expansion/backscatter_io.json (66%) rename {doc => documentation/website}/expansion/bgpranking.json (63%) rename {doc => documentation/website}/expansion/btc_scam_check.json (57%) rename {doc => documentation/website}/expansion/btc_steroids.json (88%) rename {doc => documentation/website}/expansion/censys_enrich.json (76%) rename {doc => documentation/website}/expansion/circl_passivedns.json (61%) rename {doc => documentation/website}/expansion/circl_passivessl.json (66%) rename {doc => documentation/website}/expansion/countrycode.json (99%) rename {doc => documentation/website}/expansion/cpe.json (90%) rename {doc => documentation/website}/expansion/crowdstrike_falcon.json (83%) rename {doc => documentation/website}/expansion/cuckoo_submit.json (62%) rename {doc => documentation/website}/expansion/cve.json (77%) rename {doc => documentation/website}/expansion/cve_advanced.json (87%) rename {doc => documentation/website}/expansion/cytomic_orion.json (62%) rename {doc => documentation/website}/expansion/dbl_spamhaus.json (76%) rename {doc => documentation/website}/expansion/dns.json (90%) rename {doc => documentation/website}/expansion/docx_enrich.json (82%) rename {doc => documentation/website}/expansion/domaintools.json (80%) rename {doc => documentation/website}/expansion/eql.json (77%) rename {doc => documentation/website}/expansion/eupi.json (71%) rename {doc => documentation/website}/expansion/farsight_passivedns.json (79%) rename {doc => documentation/website}/expansion/geoip_asn.json (72%) rename {doc => documentation/website}/expansion/geoip_city.json (74%) rename {doc => documentation/website}/expansion/geoip_country.json (72%) rename {doc => documentation/website}/expansion/google_search.json (64%) create mode 100644 documentation/website/expansion/greynoise.json rename {doc => documentation/website}/expansion/hashdd.json (86%) rename {doc => documentation/website}/expansion/hibp.json (83%) rename {doc => documentation/website}/expansion/html_to_markdown.json (84%) rename {doc => documentation/website}/expansion/intel471.json (79%) rename {doc => documentation/website}/expansion/intelmq_eventdb.json (57%) rename {doc => documentation/website}/expansion/ipasn.json (70%) rename {doc => documentation/website}/expansion/iprep.json (71%) rename {doc => documentation/website}/expansion/joesandbox_query.json (84%) rename {doc => documentation/website}/expansion/joesandbox_submit.json (77%) rename {doc => documentation/website}/expansion/lastline_query.json (89%) rename {doc => documentation/website}/expansion/lastline_submit.json (85%) rename {doc => documentation/website}/expansion/macaddress_io.json (60%) rename {doc => documentation/website}/expansion/macvendors.json (73%) rename {doc => documentation/website}/expansion/malwarebazaar.json (91%) rename {doc => documentation/website}/expansion/ocr_enrich.json (85%) rename {doc => documentation/website}/expansion/ods_enrich.json (65%) rename {doc => documentation/website}/expansion/odt_enrich.json (80%) rename {doc => documentation/website}/expansion/onyphe.json (61%) rename {doc => documentation/website}/expansion/onyphe_full.json (69%) rename {doc => documentation/website}/expansion/otx.json (77%) rename {doc => documentation/website}/expansion/passivetotal.json (82%) rename {doc => documentation/website}/expansion/pdf_enrich.json (76%) rename {doc => documentation/website}/expansion/pptx_enrich.json (77%) rename {doc => documentation/website}/expansion/qrcode.json (72%) rename {doc => documentation/website}/expansion/ransomcoindb.json (82%) rename {doc => documentation/website}/expansion/rbl.json (65%) rename {doc => documentation/website}/expansion/recordedfuture.json (85%) rename {doc => documentation/website}/expansion/reversedns.json (90%) rename {doc => documentation/website}/expansion/securitytrails.json (77%) rename {doc => documentation/website}/expansion/shodan.json (61%) rename {doc => documentation/website}/expansion/sigma_queries.json (69%) rename {doc => documentation/website}/expansion/sigma_syntax_validator.json (67%) rename {doc => documentation/website}/expansion/sophoslabs_intelix.json (70%) rename {doc => documentation/website}/expansion/sourcecache.json (67%) rename {doc => documentation/website}/expansion/stix2_pattern_syntax_validator.json (60%) rename {doc => documentation/website}/expansion/threatcrowd.json (87%) rename {doc => documentation/website}/expansion/threatminer.json (87%) rename {doc => documentation/website}/expansion/trustar_enrich.json (81%) rename {doc => documentation/website}/expansion/urlhaus.json (86%) rename {doc => documentation/website}/expansion/urlscan.json (73%) rename {doc => documentation/website}/expansion/virustotal.json (80%) rename {doc => documentation/website}/expansion/virustotal_public.json (78%) rename {doc => documentation/website}/expansion/vmray_submit.json (74%) rename {doc => documentation/website}/expansion/vulndb.json (71%) rename {doc => documentation/website}/expansion/vulners.json (69%) rename {doc => documentation/website}/expansion/whois.json (77%) rename {doc => documentation/website}/expansion/wiki.json (72%) rename {doc => documentation/website}/expansion/xforceexchange.json (73%) rename {doc => documentation/website}/expansion/xlsx_enrich.json (73%) rename {doc => documentation/website}/expansion/yara_query.json (77%) rename {doc => documentation/website}/expansion/yara_syntax_validator.json (70%) create mode 100644 documentation/website/export_mod/cef_export.json rename {doc => documentation/website}/export_mod/cisco_firesight_manager_ACL_rule_export.json (79%) create mode 100644 documentation/website/export_mod/goamlexport.json create mode 100644 documentation/website/export_mod/liteexport.json rename {doc => documentation/website}/export_mod/mass_eql_export.json (74%) create mode 100644 documentation/website/export_mod/nexthinkexport.json create mode 100644 documentation/website/export_mod/osqueryexport.json create mode 100644 documentation/website/export_mod/pdfexport.json rename {doc => documentation/website}/export_mod/testexport.json (95%) create mode 100644 documentation/website/export_mod/threatStream_misp_export.json create mode 100644 documentation/website/export_mod/threat_connect_export.json rename {doc => documentation/website}/export_mod/vt_graph.json (66%) create mode 100644 documentation/website/import_mod/csvimport.json create mode 100644 documentation/website/import_mod/cuckooimport.json create mode 100644 documentation/website/import_mod/email_import.json create mode 100644 documentation/website/import_mod/goamlimport.json rename {doc => documentation/website}/import_mod/joe_import.json (78%) rename {doc => documentation/website}/import_mod/lastline_import.json (86%) create mode 100644 documentation/website/import_mod/mispjson.json create mode 100644 documentation/website/import_mod/ocr.json create mode 100644 documentation/website/import_mod/openiocimport.json create mode 100644 documentation/website/import_mod/threatanalyzer_import.json create mode 100644 documentation/website/import_mod/vmray_import.json diff --git a/doc/expansion/greynoise.json b/doc/expansion/greynoise.json deleted file mode 100644 index 49ba481..0000000 --- a/doc/expansion/greynoise.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to access GreyNoise.io API", - "logo": "logos/greynoise.png", - "requirements": ["A Greynoise API key."], - "input": "An IP address.", - "output": "Additional information about the IP fetched from Greynoise API.", - "references": ["https://greynoise.io/", "https://github.com/GreyNoise-Intelligence/api.greynoise.io"], - "features": "The module takes an IP address as input and queries Greynoise for some additional information about it: basically it checks whether a given IP address is “Internet background noise”, or has been observed scanning or attacking devices across the Internet. The result is returned as text." -} diff --git a/doc/export_mod/cef_export.json b/doc/export_mod/cef_export.json deleted file mode 100644 index 84bba8e..0000000 --- a/doc/export_mod/cef_export.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to export a MISP event in CEF format.", - "requirements": [], - "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in Common Event Format.\nThus, there is no particular feature concerning MISP Events since any event can be exported. However, 4 configuration parameters recognized by CEF format are required and should be provided by users before exporting data: the device vendor, product and version, as well as the default severity of data.", - "references": ["https://community.softwaregrp.com/t5/ArcSight-Connectors/ArcSight-Common-Event-Format-CEF-Guide/ta-p/1589306?attachment-id=65537"], - "input": "MISP Event attributes", - "output": "Common Event Format file" -} diff --git a/doc/export_mod/goamlexport.json b/doc/export_mod/goamlexport.json deleted file mode 100644 index 57a1587..0000000 --- a/doc/export_mod/goamlexport.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "This module is used to export MISP events containing transaction objects into GoAML format.", - "logo": "logos/goAML.jpg", - "requirements": ["PyMISP","MISP objects"], - "features": "The module works as long as there is at least one transaction object in the Event.\n\nThen in order to have a valid GoAML document, please follow these guidelines:\n- For each transaction object, use either a bank-account, person, or legal-entity object to describe the origin of the transaction, and again one of them to describe the target of the transaction.\n- Create an object reference for both origin and target objects of the transaction.\n- A bank-account object needs a signatory, which is a person object, put as object reference of the bank-account.\n- A person can have an address, which is a geolocation object, put as object reference of the person.\n\nSupported relation types for object references that are recommended for each object are the folowing:\n- transaction:\n\t- 'from', 'from_my_client': Origin of the transaction - at least one of them is required.\n\t- 'to', 'to_my_client': Target of the transaction - at least one of them is required.\n\t- 'address': Location of the transaction - optional.\n- bank-account:\n\t- 'signatory': Signatory of a bank-account - the reference from bank-account to a signatory is required, but the relation-type is optional at the moment since this reference will always describe a signatory.\n\t- 'entity': Entity owning the bank account - optional.\n- person:\n\t- 'address': Address of a person - optional.", - "references": ["http://goaml.unodc.org/"], - "input": "MISP objects (transaction, bank-account, person, legal-entity, geolocation), with references, describing financial transactions and their origin and target.", - "output": "GoAML format file, describing financial transactions, with their origin and target (bank accounts, persons or entities)." -} diff --git a/doc/export_mod/liteexport.json b/doc/export_mod/liteexport.json deleted file mode 100644 index 110577c..0000000 --- a/doc/export_mod/liteexport.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Lite export of a MISP event.", - "requirements": [], - "features": "This module is simply producing a json MISP event format file, but exporting only Attributes from the Event. Thus, MISP Events exported with this module should have attributes that are not internal references, otherwise the resulting event would be empty.", - "references": [], - "input": "MISP Event attributes", - "output": "Lite MISP Event" -} diff --git a/doc/export_mod/nexthinkexport.json b/doc/export_mod/nexthinkexport.json deleted file mode 100644 index 182448c..0000000 --- a/doc/export_mod/nexthinkexport.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Nexthink NXQL query export module", - "requirements": [], - "features": "This module export an event as Nexthink NXQL queries that can then be used in your own python3 tool or from wget/powershell", - "references": ["https://doc.nexthink.com/Documentation/Nexthink/latest/APIAndIntegrations/IntroducingtheWebAPIV2"], - "input": "MISP Event attributes", - "output": "Nexthink NXQL queries", - "logo": "logos/nexthink.svg" -} diff --git a/doc/export_mod/osqueryexport.json b/doc/export_mod/osqueryexport.json deleted file mode 100644 index 6543cb1..0000000 --- a/doc/export_mod/osqueryexport.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "OSQuery export of a MISP event.", - "requirements": [], - "features": "This module export an event as osquery queries that can be used in packs or in fleet management solution like Kolide.", - "references": [], - "input": "MISP Event attributes", - "output": "osquery SQL queries", - "logo": "logos/osquery.png" -} diff --git a/doc/export_mod/pdfexport.json b/doc/export_mod/pdfexport.json deleted file mode 100644 index f1654dc..0000000 --- a/doc/export_mod/pdfexport.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Simple export of a MISP event to PDF.", - "requirements": ["PyMISP", "reportlab"], - "features": "The module takes care of the PDF file building, and work with any MISP Event. Except the requirement of reportlab, used to create the file, there is no special feature concerning the Event. Some parameters can be given through the config dict. 'MISP_base_url_for_dynamic_link' is your MISP URL, to attach an hyperlink to your event on your MISP instance from the PDF. Keep it clear to avoid hyperlinks in the generated pdf.\n 'MISP_name_for_metadata' is your CERT or MISP instance name. Used as text in the PDF' metadata\n 'Activate_textual_description' is a boolean (True or void) to activate the textual description/header abstract of an event\n 'Activate_galaxy_description' is a boolean (True or void) to activate the description of event related galaxies.\n 'Activate_related_events' is a boolean (True or void) to activate the description of related event. Be aware this might leak information on confidential events linked to the current event !\n 'Activate_internationalization_fonts' is a boolean (True or void) to activate Noto fonts instead of default fonts (Helvetica). This allows the support of CJK alphabet. Be sure to have followed the procedure to download Noto fonts (~70Mo) in the right place (/tools/pdf_fonts/Noto_TTF), to allow PyMisp to find and use them during PDF generation.\n 'Custom_fonts_path' is a text (path or void) to the TTF file of your choice, to create the PDF with it. Be aware the PDF won't support bold/italic/special style anymore with this option ", - "references": ["https://acrobat.adobe.com/us/en/acrobat/about-adobe-pdf.html"], - "input": "MISP Event", - "output": "MISP Event in a PDF file." -} diff --git a/doc/export_mod/threatStream_misp_export.json b/doc/export_mod/threatStream_misp_export.json deleted file mode 100644 index 3fdc50a..0000000 --- a/doc/export_mod/threatStream_misp_export.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to export a structured CSV file for uploading to threatStream.", - "logo": "logos/threatstream.png", - "requirements": ["csv"], - "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in a CSV format recognized by ThreatStream.", - "references": ["https://www.anomali.com/platform/threatstream", "https://github.com/threatstream"], - "input": "MISP Event attributes", - "output": "ThreatStream CSV format file" -} diff --git a/doc/export_mod/threat_connect_export.json b/doc/export_mod/threat_connect_export.json deleted file mode 100644 index 8d19572..0000000 --- a/doc/export_mod/threat_connect_export.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to export a structured CSV file for uploading to ThreatConnect.", - "logo": "logos/threatconnect.png", - "requirements": ["csv"], - "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in a CSV format recognized by ThreatConnect.\nUsers should then provide, as module configuration, the source of data they export, because it is required by the output format.", - "references": ["https://www.threatconnect.com"], - "input": "MISP Event attributes", - "output": "ThreatConnect CSV format file" -} diff --git a/doc/generate_documentation.py b/doc/generate_documentation.py deleted file mode 100644 index e7defa1..0000000 --- a/doc/generate_documentation.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import json - -module_types = ['expansion', 'export_mod', 'import_mod'] -titles = ['Expansion Modules', 'Export Modules', 'Import Modules'] -markdown = ["# MISP modules documentation\n"] -githublink = 'https://github.com/MISP/misp-modules/tree/main/misp_modules/modules' - - -def generate_doc(root_path): - for _path, title in zip(module_types, titles): - markdown.append('\n## {}\n'.format(title)) - current_path = os.path.join(root_path, _path) - files = sorted(os.listdir(current_path)) - githubpath = '{}/{}'.format(githublink, _path) - for _file in files: - modulename = _file.split('.json')[0] - githubref = '{}/{}.py'.format(githubpath, modulename) - markdown.append('\n#### [{}]({})\n'.format(modulename, githubref)) - filename = os.path.join(current_path, _file) - with open(filename, 'rt') as f: - definition = json.loads(f.read()) - if 'logo' in definition: - markdown.append('\n\n'.format(definition.pop('logo'))) - if 'description' in definition: - markdown.append('\n{}\n'.format(definition.pop('description'))) - for field, value in sorted(definition.items()): - if value: - value = ', '.join(value) if isinstance(value, list) else '{}'.format(value.replace('\n', '\n>')) - markdown.append('- **{}**:\n>{}\n'.format(field, value)) - markdown.append('\n-----\n') - with open('README.md', 'w') as w: - w.write(''.join(markdown)) - -def generate_docs_for_mkdocs(root_path): - for _path, title in zip(module_types, titles): - markdown = [] - #markdown.append('## {}\n'.format(title)) - current_path = os.path.join(root_path, _path) - files = sorted(os.listdir(current_path)) - githubpath = '{}/{}'.format(githublink, _path) - for _file in files: - modulename = _file.split('.json')[0] - githubref = '{}/{}.py'.format(githubpath, modulename) - markdown.append('\n#### [{}]({})\n'.format(modulename, githubref)) - filename = os.path.join(current_path, _file) - with open(filename, 'rt') as f: - definition = json.loads(f.read()) - if 'logo' in definition: - markdown.append('\n\n'.format(definition.pop('logo'))) - if 'description' in definition: - markdown.append('\n{}\n'.format(definition.pop('description'))) - for field, value in sorted(definition.items()): - if value: - value = ', '.join(value) if isinstance(value, list) else '{}'.format(value.replace('\n', '\n>')) - markdown.append('- **{}**:\n>{}\n'.format(field, value)) - markdown.append('\n-----\n') - with open(root_path+"/../"+"/docs/"+_path+".md", 'w') as w: - w.write(''.join(markdown)) - -if __name__ == '__main__': - root_path = os.path.dirname(os.path.realpath(__file__)) - generate_doc(root_path) - generate_docs_for_mkdocs(root_path) diff --git a/doc/import_mod/csvimport.json b/doc/import_mod/csvimport.json deleted file mode 100644 index 66a10fd..0000000 --- a/doc/import_mod/csvimport.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to import MISP attributes from a csv file.", - "requirements": ["PyMISP"], - "features": "In order to parse data from a csv file, a header is required to let the module know which column is matching with known attribute fields / MISP types.\n\nThis header either comes from the csv file itself or is part of the configuration of the module and should be filled out in MISP plugin settings, each field separated by COMMAS. Fields that do not match with any type known in MISP or are not MISP attribute fields should be ignored in import, using a space or simply nothing between two separators (example: 'ip-src, , comment, ').\n\nIf the csv file already contains a header that does not start by a '#', you should tick the checkbox 'has_header' to avoid importing it and have potential issues. You can also redefine the header even if it is already contained in the file, by following the rules for headers explained earlier. One reason why you would redefine a header is for instance when you want to skip some fields, or some fields are not valid types.", - "references": ["https://tools.ietf.org/html/rfc4180", "https://tools.ietf.org/html/rfc7111"], - "input": "CSV format file.", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/cuckooimport.json b/doc/import_mod/cuckooimport.json deleted file mode 100644 index 8091d07..0000000 --- a/doc/import_mod/cuckooimport.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to import Cuckoo JSON.", - "logo": "logos/cuckoo.png", - "requirements": [], - "features": "The module simply imports MISP Attributes from a Cuckoo JSON format file. There is thus no special feature to make it work.", - "references": ["https://cuckoosandbox.org/", "https://github.com/cuckoosandbox/cuckoo"], - "input": "Cuckoo JSON file", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/email_import.json b/doc/import_mod/email_import.json deleted file mode 100644 index 1f53852..0000000 --- a/doc/import_mod/email_import.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to import emails in MISP.", - "requirements": [], - "features": "This module can be used to import e-mail text as well as attachments and urls.\n3 configuration parameters are then used to unzip attachments, guess zip attachment passwords, and extract urls: set each one of them to True or False to process or not the respective corresponding actions.", - "references": [], - "input": "E-mail file", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/goamlimport.json b/doc/import_mod/goamlimport.json deleted file mode 100644 index f2a1ec2..0000000 --- a/doc/import_mod/goamlimport.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to import MISP objects about financial transactions from GoAML files.", - "logo": "logos/goAML.jpg", - "requirements": ["PyMISP"], - "features": "Unlike the GoAML export module, there is here no special feature to import data from GoAML external files, since the module will import MISP Objects with their References on its own, as it is required for the export module to rebuild a valid GoAML document.", - "references": "http://goaml.unodc.org/", - "input": "GoAML format file, describing financial transactions, with their origin and target (bank accounts, persons or entities).", - "output": "MISP objects (transaction, bank-account, person, legal-entity, geolocation), with references, describing financial transactions and their origin and target." -} diff --git a/doc/import_mod/mispjson.json b/doc/import_mod/mispjson.json deleted file mode 100644 index dd11405..0000000 --- a/doc/import_mod/mispjson.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to import MISP JSON format for merging MISP events.", - "requirements": [], - "features": "The module simply imports MISP Attributes from an other MISP Event in order to merge events together. There is thus no special feature to make it work.", - "references": [], - "input": "MISP Event", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/ocr.json b/doc/import_mod/ocr.json deleted file mode 100644 index 14bbf0b..0000000 --- a/doc/import_mod/ocr.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Optical Character Recognition (OCR) module for MISP.", - "requirements": [], - "features": "The module tries to recognize some text from an image and import the result as a freetext attribute, there is then no special feature asked to users to make it work.", - "references": [], - "input": "Image", - "output": "freetext MISP attribute" -} diff --git a/doc/import_mod/openiocimport.json b/doc/import_mod/openiocimport.json deleted file mode 100644 index e173392..0000000 --- a/doc/import_mod/openiocimport.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to import OpenIOC packages.", - "requirements": ["PyMISP"], - "features": "The module imports MISP Attributes from OpenIOC packages, there is then no special feature for users to make it work.", - "references": ["https://www.fireeye.com/blog/threat-research/2013/10/openioc-basics.html"], - "input": "OpenIOC packages", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/threatanalyzer_import.json b/doc/import_mod/threatanalyzer_import.json deleted file mode 100644 index 40e4436..0000000 --- a/doc/import_mod/threatanalyzer_import.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "description": "Module to import ThreatAnalyzer archive.zip / analysis.json files.", - "requirements": [], - "features": "The module imports MISP Attributes from a ThreatAnalyzer format file. This file can be either ZIP, or JSON format.\nThere is by the way no special feature for users to make the module work.", - "references": ["https://www.threattrack.com/malware-analysis.aspx"], - "input": "ThreatAnalyzer format file", - "output": "MISP Event attributes" -} diff --git a/doc/import_mod/vmray_import.json b/doc/import_mod/vmray_import.json deleted file mode 100644 index b7c0dad..0000000 --- a/doc/import_mod/vmray_import.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "description": "Module to import VMRay (VTI) results.", - "logo": "logos/vmray.png", - "requirements": ["vmray_rest_api"], - "features": "The module imports MISP Attributes from VMRay format, using the VMRay api.\nUsers should then provide as the module configuration the API Key as well as the server url in order to fetch their data to import.", - "references": ["https://www.vmray.com/"], - "input": "VMRay format", - "output": "MISP Event attributes" -} diff --git a/doc/README.md b/documentation/README.md similarity index 96% rename from doc/README.md rename to documentation/README.md index 3b2bceb..9a818db 100644 --- a/doc/README.md +++ b/documentation/README.md @@ -91,8 +91,6 @@ A module to submit samples and URLs to AssemblyLine for advanced analysis, and r Query backscatter.io (https://backscatter.io/). - **features**: >The module takes a source or destination IP address as input and displays the information known by backscatter.io. -> -> - **input**: >IP addresses. - **output**: @@ -109,8 +107,6 @@ Query backscatter.io (https://backscatter.io/). Query BGP Ranking (https://bgpranking-ng.circl.lu/). - **features**: >The module takes an AS number attribute as input and displays its description as well as its ranking position in BGP Ranking for a given day. -> -> - **input**: >Autonomous system number. - **output**: @@ -182,9 +178,11 @@ Module to access CIRCL Passive DNS. - **ouput**: >Passive DNS objects related to the input attribute. - **references**: ->https://www.circl.lu/services/passive-dns/, https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/ +> - https://www.circl.lu/services/passive-dns/ +> - https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/ - **requirements**: ->pypdns: Passive DNS python library, A CIRCL passive DNS account with username & password +> - pypdns: Passive DNS python library +> - A CIRCL passive DNS account with username & password ----- @@ -204,7 +202,8 @@ Modules to access CIRCL Passive SSL. - **references**: >https://www.circl.lu/services/passive-ssl/ - **requirements**: ->pypssl: Passive SSL python library, A CIRCL passive SSL account with username & password +> - pypssl: Passive SSL python library +> - A CIRCL passive SSL account with username & password ----- @@ -222,9 +221,9 @@ Module to expand country codes. ----- -#### [cpe](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cpe.py) +#### [cpe](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/cpe.py) - + An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities. - **features**: @@ -310,7 +309,8 @@ An expansion module to submit files and URLs to Cuckoo Sandbox. - **output**: >A text field containing 'Cuckoo task id: ' - **references**: ->https://cuckoosandbox.org/, https://cuckoo.sh/docs/ +> - https://cuckoosandbox.org/ +> - https://cuckoo.sh/docs/ - **requirements**: >Access to a Cuckoo Sandbox API and an API key if the API requires it. (api_url and api_key) @@ -328,7 +328,8 @@ An expansion hover module to expand information about CVE id. - **output**: >Text giving information about the CVE related to the Vulnerability. - **references**: ->https://cve.circl.lu/, https://cve.mitre.org/ +> - https://cve.circl.lu/ +> - https://cve.mitre.org/ ----- @@ -348,7 +349,8 @@ An expansion module to query the CIRCL CVE search API for more information about - **output**: >Additional information about the vulnerability, such as its cvss score, some references, or the related weaknesses and attack patterns. - **references**: ->https://cve.circl.lu, https://cve/mitre.org/ +> - https://cve.circl.lu +> - https://cve/mitre.org/ ----- @@ -364,7 +366,8 @@ An expansion module to enrich attributes in MISP by quering the Cytomic Orion AP - **output**: >MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines. - **references**: ->https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/, https://www.cytomicmodel.com/solutions/ +> - https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/ +> - https://www.cytomicmodel.com/solutions/ - **requirements**: >Access (license) to Cytomic Orion @@ -459,7 +462,8 @@ DomainTools MISP expansion module. - **references**: >https://www.domaintools.com/ - **requirements**: ->Domaintools python library, A Domaintools API access (username & apikey) +> - Domaintools python library +> - A Domaintools API access (username & apikey) ----- @@ -495,7 +499,8 @@ A module to query the Phishing Initiative service (https://phishing-initiative.l - **references**: >https://phishing-initiative.eu/?lang=en - **requirements**: ->pyeupi: eupi python library, An access to the Phishing Initiative API (apikey & url) +> - pyeupi: eupi python library +> - An access to the Phishing Initiative API (apikey & url) ----- @@ -516,7 +521,8 @@ Module to access Farsight DNSDB Passive DNS. - **output**: >Passive-dns objects, resulting from the query on the Farsight Passive DNS API. - **references**: ->https://www.farsightsecurity.com/, https://docs.dnsdb.info/dnsdb-api/ +> - https://www.farsightsecurity.com/ +> - https://docs.dnsdb.info/dnsdb-api/ - **requirements**: >An access to the Farsight Passive DNS API (apikey) @@ -608,7 +614,8 @@ Module to access GreyNoise.io API - **output**: >Additional information about the IP fetched from Greynoise API. - **references**: ->https://greynoise.io/, https://github.com/GreyNoise-Intelligence/api.greynoise.io +> - https://greynoise.io/ +> - https://github.com/GreyNoise-Intelligence/api.greynoise.io - **requirements**: >A Greynoise API key. @@ -703,9 +710,11 @@ Module to access intelmqs eventdb. - **output**: >Text giving information about the input using IntelMQ database. - **references**: ->https://github.com/certtools/intelmq, https://intelmq.readthedocs.io/en/latest/Developers-Guide/ +> - https://github.com/certtools/intelmq +> - https://intelmq.readthedocs.io/en/latest/Developers-Guide/ - **requirements**: ->psycopg2: Python library to support PostgreSQL, An access to the IntelMQ database (username, password, hostname and database reference) +> - psycopg2: Python library to support PostgreSQL +> - An access to the IntelMQ database (username, password, hostname and database reference) ----- @@ -761,7 +770,8 @@ This url can by the way come from the result of the [joesandbox_submit expansion - **output**: >MISP attributes & objects parsed from the analysis report. - **references**: ->https://www.joesecurity.org, https://www.joesandbox.com/ +> - https://www.joesecurity.org +> - https://www.joesandbox.com/ - **requirements**: >jbxapi: Joe Sandbox API python3 library @@ -781,7 +791,8 @@ A module to submit files or URLs to Joe Sandbox for an advanced analysis, and re - **output**: >Link of the report generated in Joe Sandbox. - **references**: ->https://www.joesecurity.org, https://www.joesandbox.com/ +> - https://www.joesecurity.org +> - https://www.joesandbox.com/ - **requirements**: >jbxapi: Joe Sandbox API python3 library @@ -840,9 +851,11 @@ MISP hover module for macaddress.io - **output**: >Text containing information on the MAC address fetched from a query on macaddress.io. - **references**: ->https://macaddress.io/, https://github.com/CodeLineFi/maclookup-python +> - https://macaddress.io/ +> - https://github.com/CodeLineFi/maclookup-python - **requirements**: ->maclookup: macaddress.io python library, An access to the macaddress.io API (apikey) +> - maclookup: macaddress.io python library +> - An access to the macaddress.io API (apikey) ----- @@ -858,7 +871,8 @@ Module to access Macvendors API. - **output**: >Additional information about the MAC address. - **references**: ->https://macvendors.com/, https://macvendors.com/api +> - https://macvendors.com/ +> - https://macvendors.com/api ----- @@ -904,7 +918,8 @@ Module to extract freetext from a .ods document. - **output**: >Text and freetext parsed from the document. - **requirements**: ->ezodf: Python package to create/manipulate OpenDocumentFormat files., pandas_ods_reader: Python library to read in ODS files. +> - ezodf: Python package to create/manipulate OpenDocumentFormat files. +> - pandas_ods_reader: Python library to read in ODS files. ----- @@ -936,9 +951,11 @@ Module to process a query on Onyphe. - **output**: >MISP attributes fetched from the Onyphe query. - **references**: ->https://www.onyphe.io/, https://github.com/sebdraven/pyonyphe +> - https://www.onyphe.io/ +> - https://github.com/sebdraven/pyonyphe - **requirements**: ->onyphe python library, An access to the Onyphe API (apikey) +> - onyphe python library +> - An access to the Onyphe API (apikey) ----- @@ -956,9 +973,11 @@ Module to process a full query on Onyphe. - **output**: >MISP attributes fetched from the Onyphe query. - **references**: ->https://www.onyphe.io/, https://github.com/sebdraven/pyonyphe +> - https://www.onyphe.io/ +> - https://github.com/sebdraven/pyonyphe - **requirements**: ->onyphe python library, An access to the Onyphe API (apikey) +> - onyphe python library +> - An access to the Onyphe API (apikey) ----- @@ -1043,7 +1062,8 @@ Module to get information from AlienVault OTX. - **references**: >https://www.passivetotal.org/register - **requirements**: ->Passivetotal python library, An access to the PassiveTotal API (apikey) +> - Passivetotal python library +> - An access to the PassiveTotal API (apikey) ----- @@ -1089,7 +1109,8 @@ Module to decode QR codes. - **output**: >The URL or bitcoin address the QR code is pointing to. - **requirements**: ->cv2: The OpenCV python library., pyzbar: Python library to read QR codes. +> - cv2: The OpenCV python library. +> - pyzbar: Python library to read QR codes. ----- @@ -1194,7 +1215,8 @@ An expansion modules for SecurityTrails. - **references**: >https://securitytrails.com/ - **requirements**: ->dnstrails python library, An access to the SecurityTrails API (apikey) +> - dnstrails python library +> - An access to the SecurityTrails API (apikey) ----- @@ -1212,7 +1234,8 @@ Module to query on Shodan. - **references**: >https://www.shodan.io/ - **requirements**: ->shodan python library, An access to the Shodan API (apikey) +> - shodan python library +> - An access to the Shodan API (apikey) ----- @@ -1250,7 +1273,8 @@ An expansion hover module to perform a syntax check on sigma rules. - **references**: >https://github.com/Neo23x0/sigma/wiki - **requirements**: ->Sigma python library, Yaml python library +> - Sigma python library +> - Yaml python library ----- @@ -1470,7 +1494,8 @@ Module to get advanced information from virustotal. - **output**: >MISP attributes and objects resulting from the parsing of the VirusTotal report concerning the input attribute. - **references**: ->https://www.virustotal.com/, https://developers.virustotal.com/reference +> - https://www.virustotal.com/ +> - https://developers.virustotal.com/reference - **requirements**: >An access to the VirusTotal API (apikey), with a high request rate limit. @@ -1494,7 +1519,8 @@ Module to get information from VirusTotal. - **output**: >MISP attributes and objects resulting from the parsing of the VirusTotal report concerning the input attribute. - **references**: ->https://www.virustotal.com, https://developers.virustotal.com/reference +> - https://www.virustotal.com +> - https://developers.virustotal.com/reference - **requirements**: >An access to the VirusTotal API (apikey) @@ -1561,7 +1587,8 @@ An expansion hover module to expand information about CVE id using Vulners API. - **references**: >https://vulners.com/ - **requirements**: ->Vulners python library, An access to the Vulners API +> - Vulners python library +> - An access to the Vulners API ----- @@ -1652,7 +1679,8 @@ An expansion & hover module to translate any hash attribute into a yara rule. - **output**: >YARA rule. - **references**: ->https://virustotal.github.io/yara/, https://github.com/virustotal/yara-python +> - https://virustotal.github.io/yara/ +> - https://github.com/virustotal/yara-python - **requirements**: >yara-python python library @@ -1740,7 +1768,8 @@ This module is used to export MISP events containing transaction objects into Go - **references**: >http://goaml.unodc.org/ - **requirements**: ->PyMISP, MISP objects +> - PyMISP +> - MISP objects ----- @@ -1820,7 +1849,8 @@ Simple export of a MISP event to PDF. - **references**: >https://acrobat.adobe.com/us/en/acrobat/about-adobe-pdf.html - **requirements**: ->PyMISP, reportlab +> - PyMISP +> - reportlab ----- @@ -1842,7 +1872,8 @@ Module to export a structured CSV file for uploading to threatStream. - **output**: >ThreatStream CSV format file - **references**: ->https://www.anomali.com/platform/threatstream, https://github.com/threatstream +> - https://www.anomali.com/platform/threatstream +> - https://github.com/threatstream - **requirements**: >csv @@ -1903,7 +1934,8 @@ Module to import MISP attributes from a csv file. - **output**: >MISP Event attributes - **references**: ->https://tools.ietf.org/html/rfc4180, https://tools.ietf.org/html/rfc7111 +> - https://tools.ietf.org/html/rfc4180 +> - https://tools.ietf.org/html/rfc7111 - **requirements**: >PyMISP @@ -1921,7 +1953,8 @@ Module to import Cuckoo JSON. - **output**: >MISP Event attributes - **references**: ->https://cuckoosandbox.org/, https://github.com/cuckoosandbox/cuckoo +> - https://cuckoosandbox.org/ +> - https://github.com/cuckoosandbox/cuckoo ----- @@ -1965,14 +1998,13 @@ A module to import data from a Joe Sandbox analysis json report. >Module using the new format of modules able to return attributes and objects. > >The module returns the same results as the expansion module [joesandbox_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) using the submission link of the analysis to get the json report. -> -> - **input**: >Json report of a Joe Sandbox analysis. - **output**: >MISP attributes & objects parsed from the analysis report. - **references**: ->https://www.joesecurity.org, https://www.joesandbox.com/ +> - https://www.joesecurity.org +> - https://www.joesandbox.com/ ----- diff --git a/documentation/generate_documentation.py b/documentation/generate_documentation.py new file mode 100644 index 0000000..4081e50 --- /dev/null +++ b/documentation/generate_documentation.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +import os +import json + +module_types = ['expansion', 'export_mod', 'import_mod'] +titles = ['Expansion Modules', 'Export Modules', 'Import Modules'] +githublink = 'https://github.com/MISP/misp-modules/tree/main/misp_modules/modules' + + +def generate_doc(module_type, root_path, logo_path='logos'): + markdown = [] + current_path = os.path.join(root_path, 'website', module_type) + files = sorted(os.listdir(current_path)) + githubpath = f'{githublink}/{module_type}' + for filename in files: + modulename = filename.split('.json')[0] + githubref = f'{githubpath}/{modulename}.py' + markdown.append(f'\n#### [{modulename}]({githubref})\n') + filename = os.path.join(current_path, filename) + with open(filename, 'rt') as f: + definition = json.loads(f.read()) + if 'logo' in definition: + logo = os.path.join(logo_path, definition.pop('logo')) + markdown.append(f"\n\n") + if 'description' in definition: + markdown.append(f"\n{definition.pop('description')}\n") + for field, value in sorted(definition.items()): + if not value: + continue + if isinstance(value, list): + markdown.append(handle_list(field, value)) + continue + markdown.append(get_single_value(field, value.replace('\n', '\n>'))) + markdown.append('\n-----\n') + return markdown + + +def get_single_value(field, value): + return f"- **{field}**:\n>{value}\n" + + +def handle_list(field, values): + if len(values) == 1: + return get_single_value(field, values[0]) + values = '\n> - '.join(values) + return f"- **{field}**:\n> - {values}\n" + + +def write_doc(root_path): + markdown = ["# MISP modules documentation\n"] + for _path, title in zip(module_types, titles): + markdown.append(f'\n## {title}\n') + markdown.extend(generate_doc(_path, root_path)) + with open('README.md', 'w') as w: + w.write(''.join(markdown)) + + +def write_docs_for_mkdocs(root_path): + for _path, title in zip(module_types, titles): + markdown = generate_doc(_path, root_path, logo_path='../logos') + with open(os.path.join(root_path, 'mkdocs', f'{_path}.md'), 'w') as w: + w.write(''.join(markdown)) + + +if __name__ == '__main__': + root_path = os.path.dirname(os.path.realpath(__file__)) + write_doc(root_path) + write_docs_for_mkdocs(root_path) diff --git a/doc/logos/apivoid.png b/documentation/logos/apivoid.png similarity index 100% rename from doc/logos/apivoid.png rename to documentation/logos/apivoid.png diff --git a/doc/logos/assemblyline.png b/documentation/logos/assemblyline.png similarity index 100% rename from doc/logos/assemblyline.png rename to documentation/logos/assemblyline.png diff --git a/doc/logos/backscatter_io.png b/documentation/logos/backscatter_io.png similarity index 100% rename from doc/logos/backscatter_io.png rename to documentation/logos/backscatter_io.png diff --git a/doc/logos/bitcoin.png b/documentation/logos/bitcoin.png similarity index 100% rename from doc/logos/bitcoin.png rename to documentation/logos/bitcoin.png diff --git a/doc/logos/cisco.png b/documentation/logos/cisco.png similarity index 100% rename from doc/logos/cisco.png rename to documentation/logos/cisco.png diff --git a/doc/logos/crowdstrike.png b/documentation/logos/crowdstrike.png similarity index 100% rename from doc/logos/crowdstrike.png rename to documentation/logos/crowdstrike.png diff --git a/doc/logos/cuckoo.png b/documentation/logos/cuckoo.png similarity index 100% rename from doc/logos/cuckoo.png rename to documentation/logos/cuckoo.png diff --git a/doc/logos/cve.png b/documentation/logos/cve.png similarity index 100% rename from doc/logos/cve.png rename to documentation/logos/cve.png diff --git a/doc/logos/cytomic_orion.png b/documentation/logos/cytomic_orion.png similarity index 100% rename from doc/logos/cytomic_orion.png rename to documentation/logos/cytomic_orion.png diff --git a/doc/logos/docx.png b/documentation/logos/docx.png similarity index 100% rename from doc/logos/docx.png rename to documentation/logos/docx.png diff --git a/doc/logos/domaintools.png b/documentation/logos/domaintools.png similarity index 100% rename from doc/logos/domaintools.png rename to documentation/logos/domaintools.png diff --git a/doc/logos/eql.png b/documentation/logos/eql.png similarity index 100% rename from doc/logos/eql.png rename to documentation/logos/eql.png diff --git a/doc/logos/eupi.png b/documentation/logos/eupi.png similarity index 100% rename from doc/logos/eupi.png rename to documentation/logos/eupi.png diff --git a/doc/logos/farsight.png b/documentation/logos/farsight.png similarity index 100% rename from doc/logos/farsight.png rename to documentation/logos/farsight.png diff --git a/doc/logos/goAML.jpg b/documentation/logos/goAML.jpg similarity index 100% rename from doc/logos/goAML.jpg rename to documentation/logos/goAML.jpg diff --git a/doc/logos/google.png b/documentation/logos/google.png similarity index 100% rename from doc/logos/google.png rename to documentation/logos/google.png diff --git a/doc/logos/greynoise.png b/documentation/logos/greynoise.png similarity index 100% rename from doc/logos/greynoise.png rename to documentation/logos/greynoise.png diff --git a/doc/logos/hibp.png b/documentation/logos/hibp.png similarity index 100% rename from doc/logos/hibp.png rename to documentation/logos/hibp.png diff --git a/doc/logos/intel471.png b/documentation/logos/intel471.png similarity index 100% rename from doc/logos/intel471.png rename to documentation/logos/intel471.png diff --git a/doc/logos/intelmq.png b/documentation/logos/intelmq.png similarity index 100% rename from doc/logos/intelmq.png rename to documentation/logos/intelmq.png diff --git a/doc/logos/joesandbox.png b/documentation/logos/joesandbox.png similarity index 100% rename from doc/logos/joesandbox.png rename to documentation/logos/joesandbox.png diff --git a/doc/logos/lastline.png b/documentation/logos/lastline.png similarity index 100% rename from doc/logos/lastline.png rename to documentation/logos/lastline.png diff --git a/doc/logos/macaddress_io.png b/documentation/logos/macaddress_io.png similarity index 100% rename from doc/logos/macaddress_io.png rename to documentation/logos/macaddress_io.png diff --git a/doc/logos/macvendors.png b/documentation/logos/macvendors.png similarity index 100% rename from doc/logos/macvendors.png rename to documentation/logos/macvendors.png diff --git a/doc/logos/maxmind.png b/documentation/logos/maxmind.png similarity index 100% rename from doc/logos/maxmind.png rename to documentation/logos/maxmind.png diff --git a/doc/logos/nexthink.svg b/documentation/logos/nexthink.svg similarity index 100% rename from doc/logos/nexthink.svg rename to documentation/logos/nexthink.svg diff --git a/doc/logos/ods.png b/documentation/logos/ods.png similarity index 100% rename from doc/logos/ods.png rename to documentation/logos/ods.png diff --git a/doc/logos/odt.png b/documentation/logos/odt.png similarity index 100% rename from doc/logos/odt.png rename to documentation/logos/odt.png diff --git a/doc/logos/onyphe.jpg b/documentation/logos/onyphe.jpg similarity index 100% rename from doc/logos/onyphe.jpg rename to documentation/logos/onyphe.jpg diff --git a/doc/logos/osquery.png b/documentation/logos/osquery.png similarity index 100% rename from doc/logos/osquery.png rename to documentation/logos/osquery.png diff --git a/doc/logos/otx.png b/documentation/logos/otx.png similarity index 100% rename from doc/logos/otx.png rename to documentation/logos/otx.png diff --git a/doc/logos/passivedns.png b/documentation/logos/passivedns.png similarity index 100% rename from doc/logos/passivedns.png rename to documentation/logos/passivedns.png diff --git a/doc/logos/passivessl.png b/documentation/logos/passivessl.png similarity index 100% rename from doc/logos/passivessl.png rename to documentation/logos/passivessl.png diff --git a/doc/logos/passivetotal.png b/documentation/logos/passivetotal.png similarity index 100% rename from doc/logos/passivetotal.png rename to documentation/logos/passivetotal.png diff --git a/doc/logos/pdf.jpg b/documentation/logos/pdf.jpg similarity index 100% rename from doc/logos/pdf.jpg rename to documentation/logos/pdf.jpg diff --git a/doc/logos/pptx.png b/documentation/logos/pptx.png similarity index 100% rename from doc/logos/pptx.png rename to documentation/logos/pptx.png diff --git a/doc/logos/recordedfuture.png b/documentation/logos/recordedfuture.png similarity index 100% rename from doc/logos/recordedfuture.png rename to documentation/logos/recordedfuture.png diff --git a/doc/logos/securitytrails.png b/documentation/logos/securitytrails.png similarity index 100% rename from doc/logos/securitytrails.png rename to documentation/logos/securitytrails.png diff --git a/doc/logos/shodan.png b/documentation/logos/shodan.png similarity index 100% rename from doc/logos/shodan.png rename to documentation/logos/shodan.png diff --git a/doc/logos/sigma.png b/documentation/logos/sigma.png similarity index 100% rename from doc/logos/sigma.png rename to documentation/logos/sigma.png diff --git a/doc/logos/sophoslabs_intelix.svg b/documentation/logos/sophoslabs_intelix.svg similarity index 100% rename from doc/logos/sophoslabs_intelix.svg rename to documentation/logos/sophoslabs_intelix.svg diff --git a/doc/logos/spamhaus.jpg b/documentation/logos/spamhaus.jpg similarity index 100% rename from doc/logos/spamhaus.jpg rename to documentation/logos/spamhaus.jpg diff --git a/doc/logos/stix.png b/documentation/logos/stix.png similarity index 100% rename from doc/logos/stix.png rename to documentation/logos/stix.png diff --git a/doc/logos/threatconnect.png b/documentation/logos/threatconnect.png similarity index 100% rename from doc/logos/threatconnect.png rename to documentation/logos/threatconnect.png diff --git a/doc/logos/threatcrowd.png b/documentation/logos/threatcrowd.png similarity index 100% rename from doc/logos/threatcrowd.png rename to documentation/logos/threatcrowd.png diff --git a/doc/logos/threatminer.png b/documentation/logos/threatminer.png similarity index 100% rename from doc/logos/threatminer.png rename to documentation/logos/threatminer.png diff --git a/doc/logos/threatstream.png b/documentation/logos/threatstream.png similarity index 100% rename from doc/logos/threatstream.png rename to documentation/logos/threatstream.png diff --git a/doc/logos/trustar.png b/documentation/logos/trustar.png similarity index 100% rename from doc/logos/trustar.png rename to documentation/logos/trustar.png diff --git a/doc/logos/urlhaus.png b/documentation/logos/urlhaus.png similarity index 100% rename from doc/logos/urlhaus.png rename to documentation/logos/urlhaus.png diff --git a/doc/logos/urlscan.jpg b/documentation/logos/urlscan.jpg similarity index 100% rename from doc/logos/urlscan.jpg rename to documentation/logos/urlscan.jpg diff --git a/doc/logos/virustotal.png b/documentation/logos/virustotal.png similarity index 100% rename from doc/logos/virustotal.png rename to documentation/logos/virustotal.png diff --git a/doc/logos/vmray.png b/documentation/logos/vmray.png similarity index 100% rename from doc/logos/vmray.png rename to documentation/logos/vmray.png diff --git a/doc/logos/vulndb.png b/documentation/logos/vulndb.png similarity index 100% rename from doc/logos/vulndb.png rename to documentation/logos/vulndb.png diff --git a/doc/logos/vulners.png b/documentation/logos/vulners.png similarity index 100% rename from doc/logos/vulners.png rename to documentation/logos/vulners.png diff --git a/doc/logos/wikidata.png b/documentation/logos/wikidata.png similarity index 100% rename from doc/logos/wikidata.png rename to documentation/logos/wikidata.png diff --git a/doc/logos/xforce.png b/documentation/logos/xforce.png similarity index 100% rename from doc/logos/xforce.png rename to documentation/logos/xforce.png diff --git a/doc/logos/xlsx.png b/documentation/logos/xlsx.png similarity index 100% rename from doc/logos/xlsx.png rename to documentation/logos/xlsx.png diff --git a/doc/logos/yara.png b/documentation/logos/yara.png similarity index 100% rename from doc/logos/yara.png rename to documentation/logos/yara.png diff --git a/docs/REQUIREMENTS.txt b/documentation/mkdocs/REQUIREMENTS.txt similarity index 100% rename from docs/REQUIREMENTS.txt rename to documentation/mkdocs/REQUIREMENTS.txt diff --git a/docs/contribute.md b/documentation/mkdocs/contribute.md similarity index 100% rename from docs/contribute.md rename to documentation/mkdocs/contribute.md diff --git a/docs/img/favicon.ico b/documentation/mkdocs/img/favicon.ico similarity index 100% rename from docs/img/favicon.ico rename to documentation/mkdocs/img/favicon.ico diff --git a/docs/img/misp.png b/documentation/mkdocs/img/misp.png similarity index 100% rename from docs/img/misp.png rename to documentation/mkdocs/img/misp.png diff --git a/docs/index.md b/documentation/mkdocs/index.md similarity index 100% rename from docs/index.md rename to documentation/mkdocs/index.md diff --git a/docs/install.md b/documentation/mkdocs/install.md similarity index 100% rename from docs/install.md rename to documentation/mkdocs/install.md diff --git a/docs/license.md b/documentation/mkdocs/license.md similarity index 100% rename from docs/license.md rename to documentation/mkdocs/license.md diff --git a/doc/expansion/apiosintds.json b/documentation/website/expansion/apiosintds.json similarity index 76% rename from doc/expansion/apiosintds.json rename to documentation/website/expansion/apiosintds.json index 81a1eec..8bdaf39 100644 --- a/doc/expansion/apiosintds.json +++ b/documentation/website/expansion/apiosintds.json @@ -1,8 +1,12 @@ { "description": "On demand query API for OSINT.digitalside.it project.", - "requirements": ["The apiosintDS python library to query the OSINT.digitalside.it API."], + "requirements": [ + "The apiosintDS python library to query the OSINT.digitalside.it API." + ], "input": "A domain, ip, url or hash attribute.", "output": "Hashes and urls resulting from the query to OSINT.digitalside.it", - "references": ["https://osint.digitalside.it/#About"], + "references": [ + "https://osint.digitalside.it/#About" + ], "features": "The module simply queries the API of OSINT.digitalside.it with a domain, ip, url or hash attribute.\n\nThe result of the query is then parsed to extract additional hashes or urls. A module parameters also allows to parse the hashes related to the urls.\n\nFurthermore, it is possible to cache the urls and hashes collected over the last 7 days by OSINT.digitalside.it" -} +} \ No newline at end of file diff --git a/doc/expansion/apivoid.json b/documentation/website/expansion/apivoid.json similarity index 72% rename from doc/expansion/apivoid.json rename to documentation/website/expansion/apivoid.json index 2173d5b..5962f57 100644 --- a/doc/expansion/apivoid.json +++ b/documentation/website/expansion/apivoid.json @@ -1,9 +1,13 @@ { "description": "Module to query APIVoid with some domain attributes.", - "logo": "logos/apivoid.png", - "requirements": ["A valid APIVoid API key with enough credits to proceed 2 queries"], + "logo": "apivoid.png", + "requirements": [ + "A valid APIVoid API key with enough credits to proceed 2 queries" + ], "input": "A domain attribute.", "output": "DNS records and SSL certificates related to the domain.", "features": "This module takes a domain name and queries API Void to get the related DNS records and the SSL certificates. It returns then those pieces of data as MISP objects that can be added to the event.\n\nTo make it work, a valid API key and enough credits to proceed 2 queries (0.06 + 0.07 credits) are required.", - "references": ["https://www.apivoid.com/"] -} + "references": [ + "https://www.apivoid.com/" + ] +} \ No newline at end of file diff --git a/doc/expansion/assemblyline_query.json b/documentation/website/expansion/assemblyline_query.json similarity index 78% rename from doc/expansion/assemblyline_query.json rename to documentation/website/expansion/assemblyline_query.json index 700bde0..4d54176 100644 --- a/doc/expansion/assemblyline_query.json +++ b/documentation/website/expansion/assemblyline_query.json @@ -1,9 +1,13 @@ { "description": "A module tu query the AssemblyLine API with a submission ID to get the submission report and parse it.", - "logo": "logos/assemblyline.png", - "requirements": ["assemblyline_client: Python library to query the AssemblyLine rest API."], + "logo": "assemblyline.png", + "requirements": [ + "assemblyline_client: Python library to query the AssemblyLine rest API." + ], "input": "Link of an AssemblyLine submission report.", "output": "MISP attributes & objects parsed from the AssemblyLine submission.", - "references": ["https://www.cyber.cg.ca/en/assemblyline"], + "references": [ + "https://www.cyber.cg.ca/en/assemblyline" + ], "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the used-ID and an API key or the password associated to the user-ID.\n\nThe submission ID extracted from the submission link is then used to query AssemblyLine and get the full submission report. This report is parsed to extract file objects and the associated IPs, domains or URLs the files are connecting to.\n\nSome more data may be parsed in the future." -} +} \ No newline at end of file diff --git a/doc/expansion/assemblyline_submit.json b/documentation/website/expansion/assemblyline_submit.json similarity index 72% rename from doc/expansion/assemblyline_submit.json rename to documentation/website/expansion/assemblyline_submit.json index 9fe9af6..8f147ca 100644 --- a/doc/expansion/assemblyline_submit.json +++ b/documentation/website/expansion/assemblyline_submit.json @@ -1,9 +1,13 @@ { "description": "A module to submit samples and URLs to AssemblyLine for advanced analysis, and return the link of the submission.", - "logo": "logos/assemblyline.png", - "requirements": ["assemblyline_client: Python library to query the AssemblyLine rest API."], + "logo": "assemblyline.png", + "requirements": [ + "assemblyline_client: Python library to query the AssemblyLine rest API." + ], "input": "Sample, or url to submit to AssemblyLine.", "output": "Link of the report generated in AssemblyLine.", - "references": ["https://www.cyber.gc.ca/en/assemblyline"], + "references": [ + "https://www.cyber.gc.ca/en/assemblyline" + ], "features": "The module requires the address of the AssemblyLine server you want to query as well as your credentials used for this instance. Credentials include the user-ID and an API key or the password associated to the user-ID.\n\nIf the sample or url is correctly submitted, you get then the link of the submission." -} +} \ No newline at end of file diff --git a/doc/expansion/backscatter_io.json b/documentation/website/expansion/backscatter_io.json similarity index 66% rename from doc/expansion/backscatter_io.json rename to documentation/website/expansion/backscatter_io.json index a8475c5..146e41c 100644 --- a/doc/expansion/backscatter_io.json +++ b/documentation/website/expansion/backscatter_io.json @@ -1,9 +1,13 @@ { "description": "Query backscatter.io (https://backscatter.io/).", - "requirements": ["backscatter python library"], - "features": "The module takes a source or destination IP address as input and displays the information known by backscatter.io.\n\n", - "logo": "logos/backscatter_io.png", - "references": ["https://pypi.org/project/backscatter/"], + "requirements": [ + "backscatter python library" + ], + "features": "The module takes a source or destination IP address as input and displays the information known by backscatter.io.", + "logo": "backscatter_io.png", + "references": [ + "https://pypi.org/project/backscatter/" + ], "input": "IP addresses.", "output": "Text containing a history of the IP addresses especially on scanning based on backscatter.io information ." } diff --git a/doc/expansion/bgpranking.json b/documentation/website/expansion/bgpranking.json similarity index 63% rename from doc/expansion/bgpranking.json rename to documentation/website/expansion/bgpranking.json index 4695aa1..5b0383e 100644 --- a/doc/expansion/bgpranking.json +++ b/documentation/website/expansion/bgpranking.json @@ -1,8 +1,12 @@ { "description": "Query BGP Ranking (https://bgpranking-ng.circl.lu/).", - "requirements": ["pybgpranking python library"], - "features": "The module takes an AS number attribute as input and displays its description as well as its ranking position in BGP Ranking for a given day.\n\n", - "references": ["https://github.com/D4-project/BGP-Ranking/"], + "requirements": [ + "pybgpranking python library" + ], + "features": "The module takes an AS number attribute as input and displays its description as well as its ranking position in BGP Ranking for a given day.", + "references": [ + "https://github.com/D4-project/BGP-Ranking/" + ], "input": "Autonomous system number.", "output": "An asn object with its related bgp-ranking object." } diff --git a/doc/expansion/btc_scam_check.json b/documentation/website/expansion/btc_scam_check.json similarity index 57% rename from doc/expansion/btc_scam_check.json rename to documentation/website/expansion/btc_scam_check.json index 44fce03..01fe8ff 100644 --- a/doc/expansion/btc_scam_check.json +++ b/documentation/website/expansion/btc_scam_check.json @@ -1,9 +1,13 @@ { "description": "An expansion hover module to query a special dns blacklist to check if a bitcoin address has been abused.", - "requirements": ["dnspython3: dns python library"], + "requirements": [ + "dnspython3: dns python library" + ], "features": "The module queries a dns blacklist directly with the bitcoin address and get a response if the address has been abused.", - "logo": "logos/bitcoin.png", + "logo": "bitcoin.png", "input": "btc address attribute.", - "output" : "Text to indicate if the BTC address has been abused.", - "references": ["https://btcblack.it/"] -} + "output": "Text to indicate if the BTC address has been abused.", + "references": [ + "https://btcblack.it/" + ] +} \ No newline at end of file diff --git a/doc/expansion/btc_steroids.json b/documentation/website/expansion/btc_steroids.json similarity index 88% rename from doc/expansion/btc_steroids.json rename to documentation/website/expansion/btc_steroids.json index fd264d8..b365d44 100644 --- a/doc/expansion/btc_steroids.json +++ b/documentation/website/expansion/btc_steroids.json @@ -1,6 +1,6 @@ { "description": "An expansion hover module to get a blockchain balance from a BTC address in MISP.", - "logo": "logos/bitcoin.png", + "logo": "bitcoin.png", "input": "btc address attribute.", "output": "Text to describe the blockchain balance and the transactions related to the btc address in input." -} +} \ No newline at end of file diff --git a/doc/expansion/censys_enrich.json b/documentation/website/expansion/censys_enrich.json similarity index 76% rename from doc/expansion/censys_enrich.json rename to documentation/website/expansion/censys_enrich.json index 83e6d5f..9f3a6f0 100644 --- a/doc/expansion/censys_enrich.json +++ b/documentation/website/expansion/censys_enrich.json @@ -1,8 +1,12 @@ { "description": "An expansion module to enrich attributes in MISP by quering the censys.io API", - "requirements": ["API credentials to censys.io"], + "requirements": [ + "API credentials to censys.io" + ], "input": "IP, domain or certificate fingerprint (md5, sha1 or sha256)", "output": "MISP objects retrieved from censys, including open ports, ASN, Location of the IP, x509 details", - "references": ["https://www.censys.io"], + "references": [ + "https://www.censys.io" + ], "features": "This module takes an IP, hostname or a certificate fingerprint and attempts to enrich it by querying the Censys API." -} +} \ No newline at end of file diff --git a/doc/expansion/circl_passivedns.json b/documentation/website/expansion/circl_passivedns.json similarity index 61% rename from doc/expansion/circl_passivedns.json rename to documentation/website/expansion/circl_passivedns.json index 024437c..b50136b 100644 --- a/doc/expansion/circl_passivedns.json +++ b/documentation/website/expansion/circl_passivedns.json @@ -1,9 +1,15 @@ { "description": "Module to access CIRCL Passive DNS.", - "logo": "logos/passivedns.png", - "requirements": ["pypdns: Passive DNS python library", "A CIRCL passive DNS account with username & password"], + "logo": "passivedns.png", + "requirements": [ + "pypdns: Passive DNS python library", + "A CIRCL passive DNS account with username & password" + ], "input": "Hostname, domain, or ip-address attribute.", "ouput": "Passive DNS objects related to the input attribute.", "features": "This module takes a hostname, domain or ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive DNS REST API to get the asssociated passive dns entries and return them as MISP objects.\n\nTo make it work a username and a password are thus required to authenticate to the CIRCL Passive DNS API.", - "references": ["https://www.circl.lu/services/passive-dns/", "https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/"] -} + "references": [ + "https://www.circl.lu/services/passive-dns/", + "https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/" + ] +} \ No newline at end of file diff --git a/doc/expansion/circl_passivessl.json b/documentation/website/expansion/circl_passivessl.json similarity index 66% rename from doc/expansion/circl_passivessl.json rename to documentation/website/expansion/circl_passivessl.json index f9792e1..4010297 100644 --- a/doc/expansion/circl_passivessl.json +++ b/documentation/website/expansion/circl_passivessl.json @@ -1,9 +1,14 @@ { "description": "Modules to access CIRCL Passive SSL.", - "logo": "logos/passivessl.png", - "requirements": ["pypssl: Passive SSL python library", "A CIRCL passive SSL account with username & password"], + "logo": "passivessl.png", + "requirements": [ + "pypssl: Passive SSL python library", + "A CIRCL passive SSL account with username & password" + ], "input": "IP address attribute.", "output": "x509 certificate objects seen by the IP address(es).", "features": "This module takes an ip-address (ip-src or ip-dst) attribute as input, and queries the CIRCL Passive SSL REST API to gather the related certificates and return the corresponding MISP objects.\n\nTo make it work a username and a password are required to authenticate to the CIRCL Passive SSL API.", - "references": ["https://www.circl.lu/services/passive-ssl/"] -} + "references": [ + "https://www.circl.lu/services/passive-ssl/" + ] +} \ No newline at end of file diff --git a/doc/expansion/countrycode.json b/documentation/website/expansion/countrycode.json similarity index 99% rename from doc/expansion/countrycode.json rename to documentation/website/expansion/countrycode.json index c6214e5..110bdf7 100644 --- a/doc/expansion/countrycode.json +++ b/documentation/website/expansion/countrycode.json @@ -3,4 +3,4 @@ "input": "Hostname or domain attribute.", "output": "Text with the country code the input belongs to.", "features": "The module takes a domain or a hostname as input, and returns the country it belongs to.\n\nFor non country domains, a list of the most common possible extensions is used." -} +} \ No newline at end of file diff --git a/doc/expansion/cpe.json b/documentation/website/expansion/cpe.json similarity index 90% rename from doc/expansion/cpe.json rename to documentation/website/expansion/cpe.json index 5927e5a..0160d1c 100644 --- a/doc/expansion/cpe.json +++ b/documentation/website/expansion/cpe.json @@ -1,8 +1,10 @@ { "description": "An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities.", - "logo": "logos/cpe.py", + "logo": "cve.png", "input": "CPE attribute.", "output": "The vulnerabilities related to the CPE.", - "references": ["https://cve.circl.lu/api/"], + "references": [ + "https://cve.circl.lu/api/" + ], "features": "The module takes a cpe attribute as input and queries the CVE search API to get its related vulnerabilities. \nThe list of vulnerabilities is then parsed and returned as vulnerability objects.\n\nUsers can use their own CVE search API url by defining a value to the custom_API_URL parameter. If no custom API url is given, the default cve.circl.lu api url is used.\n\nIn order to limit the amount of data returned by CVE serach, users can also the limit parameter. With the limit set, the API returns only the requested number of vulnerabilities, sorted from the highest cvss score to the lowest one." -} +} \ No newline at end of file diff --git a/doc/expansion/crowdstrike_falcon.json b/documentation/website/expansion/crowdstrike_falcon.json similarity index 83% rename from doc/expansion/crowdstrike_falcon.json rename to documentation/website/expansion/crowdstrike_falcon.json index 07e9dbd..a2408b9 100644 --- a/doc/expansion/crowdstrike_falcon.json +++ b/documentation/website/expansion/crowdstrike_falcon.json @@ -1,9 +1,13 @@ { "description": "Module to query Crowdstrike Falcon.", - "logo": "logos/crowdstrike.png", - "requirements": ["A CrowdStrike API access (API id & key)"], + "logo": "crowdstrike.png", + "requirements": [ + "A CrowdStrike API access (API id & key)" + ], "input": "A MISP attribute included in the following list:\n- domain\n- email-attachment\n- email-dst\n- email-reply-to\n- email-src\n- email-subject\n- filename\n- hostname\n- ip-src\n- ip-dst\n- md5\n- mutex\n- regkey\n- sha1\n- sha256\n- uri\n- url\n- user-agent\n- whois-registrant-email\n- x509-fingerprint-md5", "output": "MISP attributes mapped after the CrowdStrike API has been queried, included in the following list:\n- hostname\n- email-src\n- email-subject\n- filename\n- md5\n- sha1\n- sha256\n- ip-dst\n- ip-dst\n- mutex\n- regkey\n- url\n- user-agent\n- x509-fingerprint-md5", - "references": ["https://www.crowdstrike.com/products/crowdstrike-falcon-faq/"], + "references": [ + "https://www.crowdstrike.com/products/crowdstrike-falcon-faq/" + ], "features": "This module takes a MISP attribute as input to query a CrowdStrike Falcon API. The API returns then the result of the query with some types we map into compatible types we add as MISP attributes.\n\nPlease note that composite attributes composed by at least one of the input types mentionned below (domains, IPs, hostnames) are also supported." -} +} \ No newline at end of file diff --git a/doc/expansion/cuckoo_submit.json b/documentation/website/expansion/cuckoo_submit.json similarity index 62% rename from doc/expansion/cuckoo_submit.json rename to documentation/website/expansion/cuckoo_submit.json index 7fe8067..5c23218 100644 --- a/doc/expansion/cuckoo_submit.json +++ b/documentation/website/expansion/cuckoo_submit.json @@ -1,9 +1,14 @@ { "description": "An expansion module to submit files and URLs to Cuckoo Sandbox.", - "logo": "logos/cuckoo.png", - "requirements": ["Access to a Cuckoo Sandbox API and an API key if the API requires it. (api_url and api_key)"], + "logo": "cuckoo.png", + "requirements": [ + "Access to a Cuckoo Sandbox API and an API key if the API requires it. (api_url and api_key)" + ], "input": "A malware-sample or attachment for files. A url or domain for URLs.", "output": "A text field containing 'Cuckoo task id: '", - "references": ["https://cuckoosandbox.org/", "https://cuckoo.sh/docs/"], + "references": [ + "https://cuckoosandbox.org/", + "https://cuckoo.sh/docs/" + ], "features": "The module takes a malware-sample, attachment, url or domain and submits it to Cuckoo Sandbox.\n The returned task id can be used to retrieve results when the analysis completed." -} +} \ No newline at end of file diff --git a/doc/expansion/cve.json b/documentation/website/expansion/cve.json similarity index 77% rename from doc/expansion/cve.json rename to documentation/website/expansion/cve.json index 04f131f..04f5733 100644 --- a/doc/expansion/cve.json +++ b/documentation/website/expansion/cve.json @@ -1,8 +1,11 @@ { "description": "An expansion hover module to expand information about CVE id.", - "logo": "logos/cve.png", + "logo": "cve.png", "input": "Vulnerability attribute.", "output": "Text giving information about the CVE related to the Vulnerability.", - "references": ["https://cve.circl.lu/", "https://cve.mitre.org/"], + "references": [ + "https://cve.circl.lu/", + "https://cve.mitre.org/" + ], "features": "The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to get information about the vulnerability as it is described in the list of CVEs." -} +} \ No newline at end of file diff --git a/doc/expansion/cve_advanced.json b/documentation/website/expansion/cve_advanced.json similarity index 87% rename from doc/expansion/cve_advanced.json rename to documentation/website/expansion/cve_advanced.json index a4b2ac6..364fb32 100644 --- a/doc/expansion/cve_advanced.json +++ b/documentation/website/expansion/cve_advanced.json @@ -1,8 +1,11 @@ { "description": "An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE).", - "logo": "logos/cve.png", + "logo": "cve.png", "input": "Vulnerability attribute.", "output": "Additional information about the vulnerability, such as its cvss score, some references, or the related weaknesses and attack patterns.", - "references": ["https://cve.circl.lu", "https://cve/mitre.org/"], + "references": [ + "https://cve.circl.lu", + "https://cve/mitre.org/" + ], "features": "The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to gather additional information.\n\nThe result of the query is then parsed to return additional information about the vulnerability, like its cvss score or some references, as well as the potential related weaknesses and attack patterns.\n\nThe vulnerability additional data is returned in a vulnerability MISP object, and the related additional information are put into weakness and attack-pattern MISP objects." -} +} \ No newline at end of file diff --git a/doc/expansion/cytomic_orion.json b/documentation/website/expansion/cytomic_orion.json similarity index 62% rename from doc/expansion/cytomic_orion.json rename to documentation/website/expansion/cytomic_orion.json index 6f87657..8623670 100644 --- a/doc/expansion/cytomic_orion.json +++ b/documentation/website/expansion/cytomic_orion.json @@ -1,9 +1,14 @@ { "description": "An expansion module to enrich attributes in MISP by quering the Cytomic Orion API", - "logo": "logos/cytomic_orion.png", - "requirements": ["Access (license) to Cytomic Orion"], + "logo": "cytomic_orion.png", + "requirements": [ + "Access (license) to Cytomic Orion" + ], "input": "MD5, hash of the sample / malware to search for.", "output": "MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines.", - "references": ["https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/", "https://www.cytomicmodel.com/solutions/"], + "references": [ + "https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/", + "https://www.cytomicmodel.com/solutions/" + ], "features": "This module takes an MD5 hash and searches for occurrences of this hash in the Cytomic Orion database. Returns observed files and machines." -} +} \ No newline at end of file diff --git a/doc/expansion/dbl_spamhaus.json b/documentation/website/expansion/dbl_spamhaus.json similarity index 76% rename from doc/expansion/dbl_spamhaus.json rename to documentation/website/expansion/dbl_spamhaus.json index ea73dcb..6a33c8e 100644 --- a/doc/expansion/dbl_spamhaus.json +++ b/documentation/website/expansion/dbl_spamhaus.json @@ -1,9 +1,13 @@ { "description": "Module to check Spamhaus DBL for a domain name.", - "logo": "logos/spamhaus.jpg", - "requirements": ["dnspython3: DNS python3 library"], + "logo": "spamhaus.jpg", + "requirements": [ + "dnspython3: DNS python3 library" + ], "input": "Domain or hostname attribute.", "output": "Information about the nature of the input.", - "references": ["https://www.spamhaus.org/faq/section/Spamhaus%20DBL"], + "references": [ + "https://www.spamhaus.org/faq/section/Spamhaus%20DBL" + ], "features": "This modules takes a domain or a hostname in input and queries the Domain Block List provided by Spamhaus to determine what kind of domain it is.\n\nDBL then returns a response code corresponding to a certain classification of the domain we display. If the queried domain is not in the list, it is also mentionned.\n\nPlease note that composite MISP attributes containing domain or hostname are supported as well." -} +} \ No newline at end of file diff --git a/doc/expansion/dns.json b/documentation/website/expansion/dns.json similarity index 90% rename from doc/expansion/dns.json rename to documentation/website/expansion/dns.json index dc43b64..a0fb4dd 100644 --- a/doc/expansion/dns.json +++ b/documentation/website/expansion/dns.json @@ -1,7 +1,9 @@ { "description": "A simple DNS expansion service to resolve IP address from domain MISP attributes.", - "requirements": ["dnspython3: DNS python3 library"], + "requirements": [ + "dnspython3: DNS python3 library" + ], "input": "Domain or hostname attribute.", "output": "IP address resolving the input.", "features": "The module takes a domain of hostname attribute as input, and tries to resolve it. If no error is encountered, the IP address that resolves the domain is returned, otherwise the origin of the error is displayed.\n\nThe address of the DNS resolver to use is also configurable, but if no configuration is set, we use the Google public DNS address (8.8.8.8).\n\nPlease note that composite MISP attributes containing domain or hostname are supported as well." -} +} \ No newline at end of file diff --git a/doc/expansion/docx_enrich.json b/documentation/website/expansion/docx_enrich.json similarity index 82% rename from doc/expansion/docx_enrich.json rename to documentation/website/expansion/docx_enrich.json index fccba57..55bd955 100644 --- a/doc/expansion/docx_enrich.json +++ b/documentation/website/expansion/docx_enrich.json @@ -1,9 +1,11 @@ { "description": "Module to extract freetext from a .docx document.", - "logo": "logos/docx.png", - "requirements": ["docx python library"], + "logo": "docx.png", + "requirements": [ + "docx python library" + ], "input": "Attachment attribute containing a .docx document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a .docx document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/domaintools.json b/documentation/website/expansion/domaintools.json similarity index 80% rename from doc/expansion/domaintools.json rename to documentation/website/expansion/domaintools.json index 849028c..99c916b 100644 --- a/doc/expansion/domaintools.json +++ b/documentation/website/expansion/domaintools.json @@ -1,9 +1,14 @@ { "description": "DomainTools MISP expansion module.", - "logo": "logos/domaintools.png", - "requirements": ["Domaintools python library", "A Domaintools API access (username & apikey)"], + "logo": "domaintools.png", + "requirements": [ + "Domaintools python library", + "A Domaintools API access (username & apikey)" + ], "input": "A MISP attribute included in the following list:\n- domain\n- hostname\n- email-src\n- email-dst\n- target-email\n- whois-registrant-email\n- whois-registrant-name\n- whois-registrant-phone\n- ip-src\n- ip-dst", "output": "MISP attributes mapped after the Domaintools API has been queried, included in the following list:\n- whois-registrant-email\n- whois-registrant-phone\n- whois-registrant-name\n- whois-registrar\n- whois-creation-date\n- text\n- domain", - "references": ["https://www.domaintools.com/"], + "references": [ + "https://www.domaintools.com/" + ], "features": "This module takes a MISP attribute as input to query the Domaintools API. The API returns then the result of the query with some types we map into compatible types we add as MISP attributes.\n\nPlease note that composite attributes composed by at least one of the input types mentionned below (domains, IPs, hostnames) are also supported." -} +} \ No newline at end of file diff --git a/doc/expansion/eql.json b/documentation/website/expansion/eql.json similarity index 77% rename from doc/expansion/eql.json rename to documentation/website/expansion/eql.json index 1a32adf..4af9df4 100644 --- a/doc/expansion/eql.json +++ b/documentation/website/expansion/eql.json @@ -1,9 +1,11 @@ { "description": "EQL query generation for a MISP attribute.", - "logo": "logos/eql.png", + "logo": "eql.png", "requirements": [], "input": "A filename or ip attribute.", "output": "Attribute containing EQL for a network or file attribute.", - "references": ["https://eql.readthedocs.io/en/latest/"], + "references": [ + "https://eql.readthedocs.io/en/latest/" + ], "features": "This module adds a new attribute to a MISP event containing an EQL query for a network or file attribute." -} +} \ No newline at end of file diff --git a/doc/expansion/eupi.json b/documentation/website/expansion/eupi.json similarity index 71% rename from doc/expansion/eupi.json rename to documentation/website/expansion/eupi.json index 02a16fb..07eb59e 100644 --- a/doc/expansion/eupi.json +++ b/documentation/website/expansion/eupi.json @@ -1,9 +1,14 @@ { "description": "A module to query the Phishing Initiative service (https://phishing-initiative.lu).", - "logo": "logos/eupi.png", - "requirements": ["pyeupi: eupi python library", "An access to the Phishing Initiative API (apikey & url)"], + "logo": "eupi.png", + "requirements": [ + "pyeupi: eupi python library", + "An access to the Phishing Initiative API (apikey & url)" + ], "input": "A domain, hostname or url MISP attribute.", "output": "Text containing information about the input, resulting from the query on Phishing Initiative.", - "references": ["https://phishing-initiative.eu/?lang=en"], + "references": [ + "https://phishing-initiative.eu/?lang=en" + ], "features": "This module takes a domain, hostname or url MISP attribute as input to query the Phishing Initiative API. The API returns then the result of the query with some information about the value queried.\n\nPlease note that composite attributes containing domain or hostname are also supported." -} +} \ No newline at end of file diff --git a/doc/expansion/farsight_passivedns.json b/documentation/website/expansion/farsight_passivedns.json similarity index 79% rename from doc/expansion/farsight_passivedns.json rename to documentation/website/expansion/farsight_passivedns.json index 2dbc64e..ec33026 100644 --- a/doc/expansion/farsight_passivedns.json +++ b/documentation/website/expansion/farsight_passivedns.json @@ -1,9 +1,14 @@ { "description": "Module to access Farsight DNSDB Passive DNS.", - "logo": "logos/farsight.png", - "requirements": ["An access to the Farsight Passive DNS API (apikey)"], + "logo": "farsight.png", + "requirements": [ + "An access to the Farsight Passive DNS API (apikey)" + ], "input": "A domain, hostname or IP address MISP attribute.", "output": "Passive-dns objects, resulting from the query on the Farsight Passive DNS API.", - "references": ["https://www.farsightsecurity.com/", "https://docs.dnsdb.info/dnsdb-api/"], + "references": [ + "https://www.farsightsecurity.com/", + "https://docs.dnsdb.info/dnsdb-api/" + ], "features": "This module takes a domain, hostname or IP address MISP attribute as input to query the Farsight Passive DNS API.\n The results of rdata and rrset lookups are then returned and parsed into passive-dns objects.\n\nAn API key is required to submit queries to the API.\n It is also possible to define a custom server URL, and to set a limit of results to get.\n This limit is set for each lookup, which means we can have an up to the limit number of passive-dns objects resulting from an rdata query about an IP address, but an up to the limit number of passive-dns objects for each lookup queries about a domain or a hostname (== twice the limit)." -} +} \ No newline at end of file diff --git a/doc/expansion/geoip_asn.json b/documentation/website/expansion/geoip_asn.json similarity index 72% rename from doc/expansion/geoip_asn.json rename to documentation/website/expansion/geoip_asn.json index 98189c7..9a7b1dd 100644 --- a/doc/expansion/geoip_asn.json +++ b/documentation/website/expansion/geoip_asn.json @@ -1,9 +1,13 @@ { "descrption": "An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about its related AS number.", - "logo": "logos/maxmind.png", - "requirements": ["A local copy of Maxmind's Geolite database"], + "logo": "maxmind.png", + "requirements": [ + "A local copy of Maxmind's Geolite database" + ], "input": "An IP address MISP attribute.", "output": "Text containing information about the AS number of the IP address.", - "references": ["https://www.maxmind.com/en/home"], + "references": [ + "https://www.maxmind.com/en/home" + ], "features": "The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the related AS number." -} +} \ No newline at end of file diff --git a/doc/expansion/geoip_city.json b/documentation/website/expansion/geoip_city.json similarity index 74% rename from doc/expansion/geoip_city.json rename to documentation/website/expansion/geoip_city.json index bf6d8fa..24d286b 100644 --- a/doc/expansion/geoip_city.json +++ b/documentation/website/expansion/geoip_city.json @@ -1,9 +1,13 @@ { "description": "An expansion module to query a local copy of Maxmind's Geolite database with an IP address, in order to get information about the city where it is located.", - "logo": "logos/maxmind.png", - "requirements": ["A local copy of Maxmind's Geolite database"], + "logo": "maxmind.png", + "requirements": [ + "A local copy of Maxmind's Geolite database" + ], "input": "An IP address MISP attribute.", "output": "Text containing information about the city where the IP address is located.", - "references": ["https://www.maxmind.com/en/home"], + "references": [ + "https://www.maxmind.com/en/home" + ], "features": "The module takes an IP address attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the city where this IP address is located." -} +} \ No newline at end of file diff --git a/doc/expansion/geoip_country.json b/documentation/website/expansion/geoip_country.json similarity index 72% rename from doc/expansion/geoip_country.json rename to documentation/website/expansion/geoip_country.json index 9db49a2..ec84282 100644 --- a/doc/expansion/geoip_country.json +++ b/documentation/website/expansion/geoip_country.json @@ -1,9 +1,13 @@ { "description": "Module to query a local copy of Maxmind's Geolite database.", - "logo": "logos/maxmind.png", - "requirements": ["A local copy of Maxmind's Geolite database"], + "logo": "maxmind.png", + "requirements": [ + "A local copy of Maxmind's Geolite database" + ], "input": "An IP address MISP Attribute.", "output": "Text containing information about the location of the IP address.", - "references": ["https://www.maxmind.com/en/home"], + "references": [ + "https://www.maxmind.com/en/home" + ], "features": "This module takes an IP address MISP attribute as input and queries a local copy of the Maxmind's Geolite database to get information about the location of this IP address.\n\nPlease note that composite attributes domain|ip are also supported." -} +} \ No newline at end of file diff --git a/doc/expansion/google_search.json b/documentation/website/expansion/google_search.json similarity index 64% rename from doc/expansion/google_search.json rename to documentation/website/expansion/google_search.json index a3caddf..8772d21 100644 --- a/doc/expansion/google_search.json +++ b/documentation/website/expansion/google_search.json @@ -1,9 +1,13 @@ { "descrption": "A hover module to get information about an url using a Google search.", - "logo": "logos/google.png", - "requirements": ["The python Google Search API library"], + "logo": "google.png", + "requirements": [ + "The python Google Search API library" + ], "input": "An url attribute.", "output": "Text containing the result of a Google search on the input url.", - "references": ["https://github.com/abenassi/Google-Search-API"], + "references": [ + "https://github.com/abenassi/Google-Search-API" + ], "features": "The module takes an url as input to query the Google search API. The result of the query is then return as raw text." -} +} \ No newline at end of file diff --git a/documentation/website/expansion/greynoise.json b/documentation/website/expansion/greynoise.json new file mode 100644 index 0000000..4c61727 --- /dev/null +++ b/documentation/website/expansion/greynoise.json @@ -0,0 +1,14 @@ +{ + "description": "Module to access GreyNoise.io API", + "logo": "greynoise.png", + "requirements": [ + "A Greynoise API key." + ], + "input": "An IP address.", + "output": "Additional information about the IP fetched from Greynoise API.", + "references": [ + "https://greynoise.io/", + "https://github.com/GreyNoise-Intelligence/api.greynoise.io" + ], + "features": "The module takes an IP address as input and queries Greynoise for some additional information about it: basically it checks whether a given IP address is \u201cInternet background noise\u201d, or has been observed scanning or attacking devices across the Internet. The result is returned as text." +} \ No newline at end of file diff --git a/doc/expansion/hashdd.json b/documentation/website/expansion/hashdd.json similarity index 86% rename from doc/expansion/hashdd.json rename to documentation/website/expansion/hashdd.json index d963820..2edc1d1 100644 --- a/doc/expansion/hashdd.json +++ b/documentation/website/expansion/hashdd.json @@ -2,6 +2,8 @@ "description": "A hover module to check hashes against hashdd.com including NSLR dataset.", "input": "A hash MISP attribute (md5).", "output": "Text describing the known level of the hash in the hashdd databases.", - "references": ["https://hashdd.com/"], + "references": [ + "https://hashdd.com/" + ], "features": "This module takes a hash attribute as input to check its known level, using the hashdd API. This information is then displayed." -} +} \ No newline at end of file diff --git a/doc/expansion/hibp.json b/documentation/website/expansion/hibp.json similarity index 83% rename from doc/expansion/hibp.json rename to documentation/website/expansion/hibp.json index 3c3ee54..a2b7b09 100644 --- a/doc/expansion/hibp.json +++ b/documentation/website/expansion/hibp.json @@ -1,9 +1,11 @@ { "description": "Module to access haveibeenpwned.com API.", - "logo": "logos/hibp.png", + "logo": "hibp.png", "requirements": [], "input": "An email address", "output": "Additional information about the email address.", - "references": ["https://haveibeenpwned.com/"], + "references": [ + "https://haveibeenpwned.com/" + ], "features": "The module takes an email address as input and queries haveibeenpwned.com API to find additional information about it. This additional information actually tells if any account using the email address has already been compromised in a data breach." -} +} \ No newline at end of file diff --git a/doc/expansion/html_to_markdown.json b/documentation/website/expansion/html_to_markdown.json similarity index 84% rename from doc/expansion/html_to_markdown.json rename to documentation/website/expansion/html_to_markdown.json index 45f6596..0864431 100644 --- a/doc/expansion/html_to_markdown.json +++ b/documentation/website/expansion/html_to_markdown.json @@ -2,6 +2,8 @@ "description": "Expansion module to fetch the html content from an url and convert it into markdown.", "input": "URL attribute.", "output": "Markdown content converted from the HTML fetched from the url.", - "requirements": ["The markdownify python library"], + "requirements": [ + "The markdownify python library" + ], "features": "The module take an URL as input and the HTML content is fetched from it. This content is then converted into markdown that is returned as text." -} +} \ No newline at end of file diff --git a/doc/expansion/intel471.json b/documentation/website/expansion/intel471.json similarity index 79% rename from doc/expansion/intel471.json rename to documentation/website/expansion/intel471.json index 72dbaba..8935276 100644 --- a/doc/expansion/intel471.json +++ b/documentation/website/expansion/intel471.json @@ -1,9 +1,13 @@ { "descrption": "An expansion module to query Intel471 in order to get additional information about a domain, ip address, email address, url or hash.", - "logo": "logos/intel471.png", - "requirements": ["The intel471 python library"], + "logo": "intel471.png", + "requirements": [ + "The intel471 python library" + ], "input": "A MISP attribute whose type is included in the following list:\n- hostname\n- domain\n- url\n- ip-src\n- ip-dst\n- email-src\n- email-dst\n- target-email\n- whois-registrant-email\n- whois-registrant-name\n- md5\n- sha1\n- sha256", "output": "Freetext", - "references": ["https://public.intel471.com/"], + "references": [ + "https://public.intel471.com/" + ], "features": "The module uses the Intel471 python library to query the Intel471 API with the value of the input attribute. The result of the query is then returned as freetext so the Freetext import parses it." -} +} \ No newline at end of file diff --git a/doc/expansion/intelmq_eventdb.json b/documentation/website/expansion/intelmq_eventdb.json similarity index 57% rename from doc/expansion/intelmq_eventdb.json rename to documentation/website/expansion/intelmq_eventdb.json index bc48414..ce2b12a 100644 --- a/doc/expansion/intelmq_eventdb.json +++ b/documentation/website/expansion/intelmq_eventdb.json @@ -1,9 +1,15 @@ { "description": "Module to access intelmqs eventdb.", - "logo": "logos/intelmq.png", - "requirements": ["psycopg2: Python library to support PostgreSQL", "An access to the IntelMQ database (username, password, hostname and database reference)"], + "logo": "intelmq.png", + "requirements": [ + "psycopg2: Python library to support PostgreSQL", + "An access to the IntelMQ database (username, password, hostname and database reference)" + ], "input": "A hostname, domain, IP address or AS attribute.", "output": "Text giving information about the input using IntelMQ database.", - "references": ["https://github.com/certtools/intelmq", "https://intelmq.readthedocs.io/en/latest/Developers-Guide/"], + "references": [ + "https://github.com/certtools/intelmq", + "https://intelmq.readthedocs.io/en/latest/Developers-Guide/" + ], "features": "/!\\ EXPERIMENTAL MODULE, some features may not work /!\\\n\nThis module takes a domain, hostname, IP address or Autonomous system MISP attribute as input to query the IntelMQ database. The result of the query gives then additional information about the input." -} +} \ No newline at end of file diff --git a/doc/expansion/ipasn.json b/documentation/website/expansion/ipasn.json similarity index 70% rename from doc/expansion/ipasn.json rename to documentation/website/expansion/ipasn.json index 8caed92..5f30608 100644 --- a/doc/expansion/ipasn.json +++ b/documentation/website/expansion/ipasn.json @@ -1,8 +1,12 @@ { "description": "Module to query an IP ASN history service (https://github.com/D4-project/IPASN-History).", - "requirements": ["pyipasnhistory: Python library to access IPASN-history instance"], + "requirements": [ + "pyipasnhistory: Python library to access IPASN-history instance" + ], "input": "An IP address MISP attribute.", "output": "Asn object(s) objects related to the IP address used as input.", - "references": ["https://github.com/D4-project/IPASN-History"], + "references": [ + "https://github.com/D4-project/IPASN-History" + ], "features": "This module takes an IP address attribute as input and queries the CIRCL IPASN service. The result of the query is the latest asn related to the IP address, that is returned as a MISP object." -} +} \ No newline at end of file diff --git a/doc/expansion/iprep.json b/documentation/website/expansion/iprep.json similarity index 71% rename from doc/expansion/iprep.json rename to documentation/website/expansion/iprep.json index 95250e0..2e27304 100644 --- a/doc/expansion/iprep.json +++ b/documentation/website/expansion/iprep.json @@ -1,8 +1,12 @@ { "description": "Module to query IPRep data for IP addresses.", - "requirements": ["An access to the packetmail API (apikey)"], + "requirements": [ + "An access to the packetmail API (apikey)" + ], "input": "An IP address MISP attribute.", "output": "Text describing additional information about the input after a query on the IPRep API.", - "references": ["https://github.com/mahesh557/packetmail"], + "references": [ + "https://github.com/mahesh557/packetmail" + ], "features": "This module takes an IP address attribute as input and queries the database from packetmail.net to get some information about the reputation of the IP." -} +} \ No newline at end of file diff --git a/doc/expansion/joesandbox_query.json b/documentation/website/expansion/joesandbox_query.json similarity index 84% rename from doc/expansion/joesandbox_query.json rename to documentation/website/expansion/joesandbox_query.json index 1a94edb..12f2853 100644 --- a/doc/expansion/joesandbox_query.json +++ b/documentation/website/expansion/joesandbox_query.json @@ -1,9 +1,14 @@ { "description": "Query Joe Sandbox API with a submission url to get the json report and extract its data that is parsed and converted into MISP attributes and objects.\n\nThis url can by the way come from the result of the [joesandbox_submit expansion module](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_submit.py).", - "logo": "logos/joesandbox.png", - "requirements": ["jbxapi: Joe Sandbox API python3 library"], + "logo": "joesandbox.png", + "requirements": [ + "jbxapi: Joe Sandbox API python3 library" + ], "input": "Link of a Joe Sandbox sample or url submission.", "output": "MISP attributes & objects parsed from the analysis report.", - "references": ["https://www.joesecurity.org", "https://www.joesandbox.com/"], + "references": [ + "https://www.joesecurity.org", + "https://www.joesandbox.com/" + ], "features": "Module using the new format of modules able to return attributes and objects.\n\nThe module returns the same results as the import module [joe_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/joe_import.py) taking directly the json report as input.\n\nEven if the introspection will allow all kinds of links to call this module, obviously only the ones presenting a sample or url submission in the Joe Sandbox API will return results.\n\nTo make it work you will need to fill the 'apikey' configuration with your Joe Sandbox API key and provide a valid link as input." -} +} \ No newline at end of file diff --git a/doc/expansion/joesandbox_submit.json b/documentation/website/expansion/joesandbox_submit.json similarity index 77% rename from doc/expansion/joesandbox_submit.json rename to documentation/website/expansion/joesandbox_submit.json index ad59239..0ac454f 100644 --- a/doc/expansion/joesandbox_submit.json +++ b/documentation/website/expansion/joesandbox_submit.json @@ -1,9 +1,14 @@ { "description": "A module to submit files or URLs to Joe Sandbox for an advanced analysis, and return the link of the submission.", - "logo": "logos/joesandbox.png", - "requirements": ["jbxapi: Joe Sandbox API python3 library"], + "logo": "joesandbox.png", + "requirements": [ + "jbxapi: Joe Sandbox API python3 library" + ], "input": "Sample, url (or domain) to submit to Joe Sandbox for an advanced analysis.", "output": "Link of the report generated in Joe Sandbox.", - "references": ["https://www.joesecurity.org", "https://www.joesandbox.com/"], + "references": [ + "https://www.joesecurity.org", + "https://www.joesandbox.com/" + ], "features": "The module requires a Joe Sandbox API key to submit files or URL, and returns the link of the submitted analysis.\n\nIt is then possible, when the analysis is completed, to query the Joe Sandbox API to get the data related to the analysis, using the [joesandbox_query module](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) directly on this submission link." -} +} \ No newline at end of file diff --git a/doc/expansion/lastline_query.json b/documentation/website/expansion/lastline_query.json similarity index 89% rename from doc/expansion/lastline_query.json rename to documentation/website/expansion/lastline_query.json index 6165890..611b514 100644 --- a/doc/expansion/lastline_query.json +++ b/documentation/website/expansion/lastline_query.json @@ -1,9 +1,11 @@ { "description": "Query Lastline with an analysis link and parse the report into MISP attributes and objects.\nThe analysis link can also be retrieved from the output of the [lastline_submit](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_submit.py) expansion module.", - "logo": "logos/lastline.png", + "logo": "lastline.png", "requirements": [], "input": "Link to a Lastline analysis.", "output": "MISP attributes and objects parsed from the analysis report.", - "references": ["https://www.lastline.com"], + "references": [ + "https://www.lastline.com" + ], "features": "The module requires a Lastline Portal `username` and `password`.\nThe module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_import](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/import_mod/lastline_import.py) import module." -} +} \ No newline at end of file diff --git a/doc/expansion/lastline_submit.json b/documentation/website/expansion/lastline_submit.json similarity index 85% rename from doc/expansion/lastline_submit.json rename to documentation/website/expansion/lastline_submit.json index d053f55..7c4387f 100644 --- a/doc/expansion/lastline_submit.json +++ b/documentation/website/expansion/lastline_submit.json @@ -1,9 +1,11 @@ { "description": "Module to submit a file or URL to Lastline.", - "logo": "logos/lastline.png", + "logo": "lastline.png", "requirements": [], "input": "File or URL to submit to Lastline.", "output": "Link to the report generated by Lastline.", - "references": ["https://www.lastline.com"], + "references": [ + "https://www.lastline.com" + ], "features": "The module requires a Lastline Analysis `api_token` and `key`.\nWhen the analysis is completed, it is possible to import the generated report by feeding the analysis link to the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) module." -} +} \ No newline at end of file diff --git a/doc/expansion/macaddress_io.json b/documentation/website/expansion/macaddress_io.json similarity index 60% rename from doc/expansion/macaddress_io.json rename to documentation/website/expansion/macaddress_io.json index 6bd2658..013564a 100644 --- a/doc/expansion/macaddress_io.json +++ b/documentation/website/expansion/macaddress_io.json @@ -1,9 +1,15 @@ { "description": "MISP hover module for macaddress.io", - "logo": "logos/macaddress_io.png", - "requirements": ["maclookup: macaddress.io python library", "An access to the macaddress.io API (apikey)"], + "logo": "macaddress_io.png", + "requirements": [ + "maclookup: macaddress.io python library", + "An access to the macaddress.io API (apikey)" + ], "input": "MAC address MISP attribute.", "output": "Text containing information on the MAC address fetched from a query on macaddress.io.", - "references": ["https://macaddress.io/", "https://github.com/CodeLineFi/maclookup-python"], + "references": [ + "https://macaddress.io/", + "https://github.com/CodeLineFi/maclookup-python" + ], "features": "This module takes a MAC address attribute as input and queries macaddress.io for additional information.\n\nThis information contains data about:\n- MAC address details\n- Vendor details\n- Block details" -} +} \ No newline at end of file diff --git a/doc/expansion/macvendors.json b/documentation/website/expansion/macvendors.json similarity index 73% rename from doc/expansion/macvendors.json rename to documentation/website/expansion/macvendors.json index cc10475..38c3588 100644 --- a/doc/expansion/macvendors.json +++ b/documentation/website/expansion/macvendors.json @@ -1,9 +1,12 @@ { "description": "Module to access Macvendors API.", - "logo": "logos/macvendors.png", + "logo": "macvendors.png", "requirements": [], "input": "A MAC address.", "output": "Additional information about the MAC address.", - "references": ["https://macvendors.com/", "https://macvendors.com/api"], + "references": [ + "https://macvendors.com/", + "https://macvendors.com/api" + ], "features": "The module takes a MAC address as input and queries macvendors.com for some information about it. The API returns the name of the vendor related to the address." -} +} \ No newline at end of file diff --git a/doc/expansion/malwarebazaar.json b/documentation/website/expansion/malwarebazaar.json similarity index 91% rename from doc/expansion/malwarebazaar.json rename to documentation/website/expansion/malwarebazaar.json index 2db6ad5..8c8228c 100644 --- a/doc/expansion/malwarebazaar.json +++ b/documentation/website/expansion/malwarebazaar.json @@ -3,6 +3,8 @@ "requirements": [], "input": "A hash attribute (md5, sha1 or sha256).", "output": "File object(s) related to the input attribute found on MALWAREbazaar databases.", - "references": ["https://bazaar.abuse.ch/"], + "references": [ + "https://bazaar.abuse.ch/" + ], "features": "The module takes a hash attribute as input and queries MALWAREbazaar's API to fetch additional data about it. The result, if the payload is known on the databases, is at least one file object describing the file the input hash is related to.\n\nThe module is using the new format of modules able to return object since the result is one or multiple MISP object(s)." -} +} \ No newline at end of file diff --git a/doc/expansion/ocr_enrich.json b/documentation/website/expansion/ocr_enrich.json similarity index 85% rename from doc/expansion/ocr_enrich.json rename to documentation/website/expansion/ocr_enrich.json index 8765b22..0e8f627 100644 --- a/doc/expansion/ocr_enrich.json +++ b/documentation/website/expansion/ocr_enrich.json @@ -1,8 +1,10 @@ { "description": "Module to process some optical character recognition on pictures.", - "requirements": ["cv2: The OpenCV python library."], + "requirements": [ + "cv2: The OpenCV python library." + ], "input": "A picture attachment.", "output": "Text and freetext fetched from the input picture.", "references": [], "features": "The module takes an attachment attributes as input and process some optical character recognition on it. The text found is then passed to the Freetext importer to extract potential IoCs." -} +} \ No newline at end of file diff --git a/doc/expansion/ods_enrich.json b/documentation/website/expansion/ods_enrich.json similarity index 65% rename from doc/expansion/ods_enrich.json rename to documentation/website/expansion/ods_enrich.json index dda4281..ade4105 100644 --- a/doc/expansion/ods_enrich.json +++ b/documentation/website/expansion/ods_enrich.json @@ -1,10 +1,12 @@ { "description": "Module to extract freetext from a .ods document.", - "logo": "logos/ods.png", - "requirements": ["ezodf: Python package to create/manipulate OpenDocumentFormat files.", - "pandas_ods_reader: Python library to read in ODS files."], + "logo": "ods.png", + "requirements": [ + "ezodf: Python package to create/manipulate OpenDocumentFormat files.", + "pandas_ods_reader: Python library to read in ODS files." + ], "input": "Attachment attribute containing a .ods document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a .ods document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/odt_enrich.json b/documentation/website/expansion/odt_enrich.json similarity index 80% rename from doc/expansion/odt_enrich.json rename to documentation/website/expansion/odt_enrich.json index e201c77..8922a9b 100644 --- a/doc/expansion/odt_enrich.json +++ b/documentation/website/expansion/odt_enrich.json @@ -1,9 +1,11 @@ { "description": "Module to extract freetext from a .odt document.", - "logo": "logos/odt.png", - "requirements": ["ODT reader python library."], + "logo": "odt.png", + "requirements": [ + "ODT reader python library." + ], "input": "Attachment attribute containing a .odt document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a .odt document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/onyphe.json b/documentation/website/expansion/onyphe.json similarity index 61% rename from doc/expansion/onyphe.json rename to documentation/website/expansion/onyphe.json index 04ebdd3..f38ea25 100644 --- a/doc/expansion/onyphe.json +++ b/documentation/website/expansion/onyphe.json @@ -1,9 +1,15 @@ { "description": "Module to process a query on Onyphe.", - "logo": "logos/onyphe.jpg", - "requirements": ["onyphe python library", "An access to the Onyphe API (apikey)"], + "logo": "onyphe.jpg", + "requirements": [ + "onyphe python library", + "An access to the Onyphe API (apikey)" + ], "input": "A domain, hostname or IP address MISP attribute.", "output": "MISP attributes fetched from the Onyphe query.", - "references": ["https://www.onyphe.io/", "https://github.com/sebdraven/pyonyphe"], + "references": [ + "https://www.onyphe.io/", + "https://github.com/sebdraven/pyonyphe" + ], "features": "This module takes a domain, hostname, or IP address attribute as input in order to query the Onyphe API. Data fetched from the query is then parsed and MISP attributes are extracted." -} +} \ No newline at end of file diff --git a/doc/expansion/onyphe_full.json b/documentation/website/expansion/onyphe_full.json similarity index 69% rename from doc/expansion/onyphe_full.json rename to documentation/website/expansion/onyphe_full.json index 4b722fa..e1a040a 100644 --- a/doc/expansion/onyphe_full.json +++ b/documentation/website/expansion/onyphe_full.json @@ -1,9 +1,15 @@ { "description": "Module to process a full query on Onyphe.", - "logo": "logos/onyphe.jpg", - "requirements": ["onyphe python library", "An access to the Onyphe API (apikey)"], + "logo": "onyphe.jpg", + "requirements": [ + "onyphe python library", + "An access to the Onyphe API (apikey)" + ], "input": "A domain, hostname or IP address MISP attribute.", "output": "MISP attributes fetched from the Onyphe query.", - "references": ["https://www.onyphe.io/", "https://github.com/sebdraven/pyonyphe"], + "references": [ + "https://www.onyphe.io/", + "https://github.com/sebdraven/pyonyphe" + ], "features": "This module takes a domain, hostname, or IP address attribute as input in order to query the Onyphe API. Data fetched from the query is then parsed and MISP attributes are extracted.\n\nThe parsing is here more advanced than the one on onyphe module, and is returning more attributes, since more fields of the query result are watched and parsed." -} +} \ No newline at end of file diff --git a/doc/expansion/otx.json b/documentation/website/expansion/otx.json similarity index 77% rename from doc/expansion/otx.json rename to documentation/website/expansion/otx.json index c6032cc..a17e2ff 100644 --- a/doc/expansion/otx.json +++ b/documentation/website/expansion/otx.json @@ -1,9 +1,13 @@ { "description": "Module to get information from AlienVault OTX.", - "logo": "logos/otx.png", - "requirements": ["An access to the OTX API (apikey)"], + "logo": "otx.png", + "requirements": [ + "An access to the OTX API (apikey)" + ], "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512", "output": "MISP attributes mapped from the result of the query on OTX, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- email", - "references": ["https://www.alienvault.com/open-threat-exchange"], + "references": [ + "https://www.alienvault.com/open-threat-exchange" + ], "features": "This module takes a MISP attribute as input to query the OTX Alienvault API. The API returns then the result of the query with some types we map into compatible types we add as MISP attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/passivetotal.json b/documentation/website/expansion/passivetotal.json similarity index 82% rename from doc/expansion/passivetotal.json rename to documentation/website/expansion/passivetotal.json index ef8b044..26835d5 100644 --- a/doc/expansion/passivetotal.json +++ b/documentation/website/expansion/passivetotal.json @@ -1,9 +1,14 @@ { "description": "", - "logo": "logos/passivetotal.png", - "requirements": ["Passivetotal python library", "An access to the PassiveTotal API (apikey)"], + "logo": "passivetotal.png", + "requirements": [ + "Passivetotal python library", + "An access to the PassiveTotal API (apikey)" + ], "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- x509-fingerprint-sha1\n- email-src\n- email-dst\n- target-email\n- whois-registrant-email\n- whois-registrant-phone\n- text\n- whois-registrant-name\n- whois-registrar\n- whois-creation-date", "output": "MISP attributes mapped from the result of the query on PassiveTotal, included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- x509-fingerprint-sha1\n- email-src\n- email-dst\n- target-email\n- whois-registrant-email\n- whois-registrant-phone\n- text\n- whois-registrant-name\n- whois-registrar\n- whois-creation-date\n- md5\n- sha1\n- sha256\n- link", - "references": ["https://www.passivetotal.org/register"], + "references": [ + "https://www.passivetotal.org/register" + ], "features": "The PassiveTotal MISP expansion module brings the datasets derived from Internet scanning directly into your MISP instance. This module supports passive DNS, historic SSL, WHOIS, and host attributes. In order to use the module, you must have a valid PassiveTotal account username and API key. Registration is free and can be done by visiting https://www.passivetotal.org/register" -} +} \ No newline at end of file diff --git a/doc/expansion/pdf_enrich.json b/documentation/website/expansion/pdf_enrich.json similarity index 76% rename from doc/expansion/pdf_enrich.json rename to documentation/website/expansion/pdf_enrich.json index 5b3f0a8..a17ef51 100644 --- a/doc/expansion/pdf_enrich.json +++ b/documentation/website/expansion/pdf_enrich.json @@ -1,9 +1,11 @@ { "description": "Module to extract freetext from a PDF document.", - "logo": "logos/pdf.jpg", - "requirements": ["pdftotext: Python library to extract text from PDF."], + "logo": "pdf.jpg", + "requirements": [ + "pdftotext: Python library to extract text from PDF." + ], "input": "Attachment attribute containing a PDF document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a PDF document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/pptx_enrich.json b/documentation/website/expansion/pptx_enrich.json similarity index 77% rename from doc/expansion/pptx_enrich.json rename to documentation/website/expansion/pptx_enrich.json index aff0d8d..664c70a 100644 --- a/doc/expansion/pptx_enrich.json +++ b/documentation/website/expansion/pptx_enrich.json @@ -1,9 +1,11 @@ { "description": "Module to extract freetext from a .pptx document.", - "logo": "logos/pptx.png", - "requirements": ["pptx: Python library to read PowerPoint files."], + "logo": "pptx.png", + "requirements": [ + "pptx: Python library to read PowerPoint files." + ], "input": "Attachment attribute containing a .pptx document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a .pptx document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/qrcode.json b/documentation/website/expansion/qrcode.json similarity index 72% rename from doc/expansion/qrcode.json rename to documentation/website/expansion/qrcode.json index 38ed77c..f585511 100644 --- a/doc/expansion/qrcode.json +++ b/documentation/website/expansion/qrcode.json @@ -1,9 +1,11 @@ { "description": "Module to decode QR codes.", - "requirements": ["cv2: The OpenCV python library.", - "pyzbar: Python library to read QR codes."], + "requirements": [ + "cv2: The OpenCV python library.", + "pyzbar: Python library to read QR codes." + ], "input": "A QR code stored as attachment attribute.", "output": "The URL or bitcoin address the QR code is pointing to.", "references": [], "features": "The module reads the QR code and returns the related address, which can be an URL or a bitcoin address." -} +} \ No newline at end of file diff --git a/doc/expansion/ransomcoindb.json b/documentation/website/expansion/ransomcoindb.json similarity index 82% rename from doc/expansion/ransomcoindb.json rename to documentation/website/expansion/ransomcoindb.json index bc4e2ab..26c3c55 100644 --- a/doc/expansion/ransomcoindb.json +++ b/documentation/website/expansion/ransomcoindb.json @@ -1,8 +1,12 @@ { "descrption": "Module to access the ransomcoinDB with a hash or btc address attribute and get the associated btc address of hashes.", - "requirements": ["A ransomcoinDB API key."], + "requirements": [ + "A ransomcoinDB API key." + ], "input": "A hash (md5, sha1 or sha256) or btc attribute.", "output": "Hashes associated to a btc address or btc addresses associated to a hash.", - "references": ["https://ransomcoindb.concinnity-risks.com"], + "references": [ + "https://ransomcoindb.concinnity-risks.com" + ], "features": "The module takes either a hash attribute or a btc attribute as input to query the ransomcoinDB API for some additional data.\n\nIf the input is a btc address, we will get the associated hashes returned in a file MISP object. If we query ransomcoinDB with a hash, the response contains the associated btc addresses returned as single MISP btc attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/rbl.json b/documentation/website/expansion/rbl.json similarity index 65% rename from doc/expansion/rbl.json rename to documentation/website/expansion/rbl.json index 9700eca..942daa7 100644 --- a/doc/expansion/rbl.json +++ b/documentation/website/expansion/rbl.json @@ -1,8 +1,12 @@ { "description": "Module to check an IPv4 address against known RBLs.", - "requirements": ["dnspython3: DNS python3 library"], + "requirements": [ + "dnspython3: DNS python3 library" + ], "input": "IP address attribute.", "output": "Text with additional data from Real-time Blackhost Lists about the IP address.", - "references": ["[RBLs list](https://github.com/MISP/misp-modules/blob/8817de476572a10a9c9d03258ec81ca70f3d926d/misp_modules/modules/expansion/rbl.py#L20)"], + "references": [ + "[RBLs list](https://github.com/MISP/misp-modules/blob/8817de476572a10a9c9d03258ec81ca70f3d926d/misp_modules/modules/expansion/rbl.py#L20)" + ], "features": "This module takes an IP address attribute as input and queries multiple know Real-time Blackhost Lists to check if they have already seen this IP address.\n\nWe display then all the information we get from those different sources." -} +} \ No newline at end of file diff --git a/doc/expansion/recordedfuture.json b/documentation/website/expansion/recordedfuture.json similarity index 85% rename from doc/expansion/recordedfuture.json rename to documentation/website/expansion/recordedfuture.json index 2fec7eb..91cf23e 100644 --- a/doc/expansion/recordedfuture.json +++ b/documentation/website/expansion/recordedfuture.json @@ -1,9 +1,13 @@ { "description": "Module to enrich attributes with threat intelligence from Recorded Future.", - "logo": "logos/recordedfuture.png", - "requirements": ["A Recorded Future API token."], + "logo": "recordedfuture.png", + "requirements": [ + "A Recorded Future API token." + ], "input": "A MISP attribute of one of the following types: ip, ip-src, ip-dst, domain, hostname, md5, sha1, sha256, uri, url, vulnerability, weakness.", "output": "A MISP object containing a copy of the enriched attribute with added tags from Recorded Future and a list of new attributes related to the enriched attribute.", - "references": ["https://www.recordedfuture.com/"], + "references": [ + "https://www.recordedfuture.com/" + ], "features": "Enrich an attribute to add a custom enrichment object to the event. The object contains a copy of the enriched attribute with added tags presenting risk score and triggered risk rules from Recorded Future. Malware and Threat Actors related to the enriched indicator in Recorded Future is matched against MISP's galaxy clusters and applied as galaxy tags. The custom enrichment object also includes a list of related indicators from Recorded Future (IP's, domains, hashes, URL's and vulnerabilities) added as additional attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/reversedns.json b/documentation/website/expansion/reversedns.json similarity index 90% rename from doc/expansion/reversedns.json rename to documentation/website/expansion/reversedns.json index 6934462..cdd3419 100644 --- a/doc/expansion/reversedns.json +++ b/documentation/website/expansion/reversedns.json @@ -1,7 +1,9 @@ { "description": "Simple Reverse DNS expansion service to resolve reverse DNS from MISP attributes.", - "requirements": ["DNS python library"], + "requirements": [ + "DNS python library" + ], "input": "An IP address attribute.", "output": "Hostname attribute the input is resolved into.", "features": "The module takes an IP address as input and tries to find the hostname this IP address is resolved into.\n\nThe address of the DNS resolver to use is also configurable, but if no configuration is set, we use the Google public DNS address (8.8.8.8).\n\nPlease note that composite MISP attributes containing IP addresses are supported as well." -} +} \ No newline at end of file diff --git a/doc/expansion/securitytrails.json b/documentation/website/expansion/securitytrails.json similarity index 77% rename from doc/expansion/securitytrails.json rename to documentation/website/expansion/securitytrails.json index 8541e4e..97f81b4 100644 --- a/doc/expansion/securitytrails.json +++ b/documentation/website/expansion/securitytrails.json @@ -1,9 +1,14 @@ { "description": "An expansion modules for SecurityTrails.", - "logo": "logos/securitytrails.png", - "requirements": ["dnstrails python library", "An access to the SecurityTrails API (apikey)"], + "logo": "securitytrails.png", + "requirements": [ + "dnstrails python library", + "An access to the SecurityTrails API (apikey)" + ], "input": "A domain, hostname or IP address attribute.", "output": "MISP attributes resulting from the query on SecurityTrails API, included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- dns-soa-email\n- whois-registrant-email\n- whois-registrant-phone\n- whois-registrant-name\n- whois-registrar\n- whois-creation-date\n- domain", - "references": ["https://securitytrails.com/"], + "references": [ + "https://securitytrails.com/" + ], "features": "The module takes a domain, hostname or IP address attribute as input and queries the SecurityTrails API with it.\n\nMultiple parsing operations are then processed on the result of the query to extract a much information as possible.\n\nFrom this data extracted are then mapped MISP attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/shodan.json b/documentation/website/expansion/shodan.json similarity index 61% rename from doc/expansion/shodan.json rename to documentation/website/expansion/shodan.json index 57241f0..703a084 100644 --- a/doc/expansion/shodan.json +++ b/documentation/website/expansion/shodan.json @@ -1,9 +1,14 @@ { "description": "Module to query on Shodan.", - "logo": "logos/shodan.png", - "requirements": ["shodan python library", "An access to the Shodan API (apikey)"], + "logo": "shodan.png", + "requirements": [ + "shodan python library", + "An access to the Shodan API (apikey)" + ], "input": "An IP address MISP attribute.", "output": "Text with additional data about the input, resulting from the query on Shodan.", - "references": ["https://www.shodan.io/"], + "references": [ + "https://www.shodan.io/" + ], "features": "The module takes an IP address as input and queries the Shodan API to get some additional data about it." -} +} \ No newline at end of file diff --git a/doc/expansion/sigma_queries.json b/documentation/website/expansion/sigma_queries.json similarity index 69% rename from doc/expansion/sigma_queries.json rename to documentation/website/expansion/sigma_queries.json index f127ba4..c967112 100644 --- a/doc/expansion/sigma_queries.json +++ b/documentation/website/expansion/sigma_queries.json @@ -1,9 +1,13 @@ { "description": "An expansion hover module to display the result of sigma queries.", - "logo": "logos/sigma.png", - "requirements": ["Sigma python library"], + "logo": "sigma.png", + "requirements": [ + "Sigma python library" + ], "input": "A Sigma attribute.", "output": "Text displaying results of queries on the Sigma attribute.", - "references": ["https://github.com/Neo23x0/sigma/wiki"], + "references": [ + "https://github.com/Neo23x0/sigma/wiki" + ], "features": "This module takes a Sigma rule attribute as input and tries all the different queries available to convert it into different formats recognized by SIEMs." -} +} \ No newline at end of file diff --git a/doc/expansion/sigma_syntax_validator.json b/documentation/website/expansion/sigma_syntax_validator.json similarity index 67% rename from doc/expansion/sigma_syntax_validator.json rename to documentation/website/expansion/sigma_syntax_validator.json index 8e17ae0..b90c931 100644 --- a/doc/expansion/sigma_syntax_validator.json +++ b/documentation/website/expansion/sigma_syntax_validator.json @@ -1,9 +1,14 @@ { "description": "An expansion hover module to perform a syntax check on sigma rules.", - "logo": "logos/sigma.png", - "requirements": ["Sigma python library", "Yaml python library"], + "logo": "sigma.png", + "requirements": [ + "Sigma python library", + "Yaml python library" + ], "input": "A Sigma attribute.", "output": "Text describing the validity of the Sigma rule.", - "references": ["https://github.com/Neo23x0/sigma/wiki"], + "references": [ + "https://github.com/Neo23x0/sigma/wiki" + ], "features": "This module takes a Sigma rule attribute as input and performs a syntax check on it.\n\nIt displays then that the rule is valid if it is the case, and the error related to the rule otherwise." -} +} \ No newline at end of file diff --git a/doc/expansion/sophoslabs_intelix.json b/documentation/website/expansion/sophoslabs_intelix.json similarity index 70% rename from doc/expansion/sophoslabs_intelix.json rename to documentation/website/expansion/sophoslabs_intelix.json index 18dd7c1..8871192 100644 --- a/doc/expansion/sophoslabs_intelix.json +++ b/documentation/website/expansion/sophoslabs_intelix.json @@ -1,9 +1,13 @@ { "description": "An expansion module to query the Sophoslabs intelix API to get additional information about an ip address, url, domain or sha256 attribute.", - "logo": "logos/sophoslabs_intelix.svg", - "requirements": ["A client_id and client_secret pair to authenticate to the SophosLabs Intelix API"], + "logo": "sophoslabs_intelix.svg", + "requirements": [ + "A client_id and client_secret pair to authenticate to the SophosLabs Intelix API" + ], "input": "An ip address, url, domain or sha256 attribute.", "output": "SophosLabs Intelix report and lookup objects", - "references": ["https://aws.amazon.com/marketplace/pp/B07SLZPMCS"], + "references": [ + "https://aws.amazon.com/marketplace/pp/B07SLZPMCS" + ], "features": "The module takes an ip address, url, domain or sha256 attribute and queries the SophosLabs Intelix API with the attribute value. The result of this query is a SophosLabs Intelix hash report, or an ip or url lookup, that is then parsed and returned in a MISP object." -} +} \ No newline at end of file diff --git a/doc/expansion/sourcecache.json b/documentation/website/expansion/sourcecache.json similarity index 67% rename from doc/expansion/sourcecache.json rename to documentation/website/expansion/sourcecache.json index ab4669c..4340f2c 100644 --- a/doc/expansion/sourcecache.json +++ b/documentation/website/expansion/sourcecache.json @@ -1,8 +1,12 @@ { "description": "Module to cache web pages of analysis reports, OSINT sources. The module returns a link of the cached page.", - "requirements": ["urlarchiver: python library to fetch and archive URL on the file-system"], + "requirements": [ + "urlarchiver: python library to fetch and archive URL on the file-system" + ], "input": "A link or url attribute.", "output": "A malware-sample attribute describing the cached page.", - "references": ["https://github.com/adulau/url_archiver"], + "references": [ + "https://github.com/adulau/url_archiver" + ], "features": "This module takes a link or url attribute as input and caches the related web page. It returns then a link of the cached page." -} +} \ No newline at end of file diff --git a/doc/expansion/stix2_pattern_syntax_validator.json b/documentation/website/expansion/stix2_pattern_syntax_validator.json similarity index 60% rename from doc/expansion/stix2_pattern_syntax_validator.json rename to documentation/website/expansion/stix2_pattern_syntax_validator.json index 2ea43b5..0ac079d 100644 --- a/doc/expansion/stix2_pattern_syntax_validator.json +++ b/documentation/website/expansion/stix2_pattern_syntax_validator.json @@ -1,9 +1,13 @@ { "description": "An expansion hover module to perform a syntax check on stix2 patterns.", - "logo": "logos/stix.png", - "requirements": ["stix2patterns python library"], + "logo": "stix.png", + "requirements": [ + "stix2patterns python library" + ], "input": "A STIX2 pattern attribute.", "output": "Text describing the validity of the STIX2 pattern.", - "references": ["[STIX2.0 patterning specifications](http://docs.oasis-open.org/cti/stix/v2.0/cs01/part5-stix-patterning/stix-v2.0-cs01-part5-stix-patterning.html)"], + "references": [ + "[STIX2.0 patterning specifications](http://docs.oasis-open.org/cti/stix/v2.0/cs01/part5-stix-patterning/stix-v2.0-cs01-part5-stix-patterning.html)" + ], "features": "This module takes a STIX2 pattern attribute as input and performs a syntax check on it.\n\nIt displays then that the rule is valid if it is the case, and the error related to the rule otherwise." -} +} \ No newline at end of file diff --git a/doc/expansion/threatcrowd.json b/documentation/website/expansion/threatcrowd.json similarity index 87% rename from doc/expansion/threatcrowd.json rename to documentation/website/expansion/threatcrowd.json index 99725b8..e279ece 100644 --- a/doc/expansion/threatcrowd.json +++ b/documentation/website/expansion/threatcrowd.json @@ -1,8 +1,10 @@ { "description": "Module to get information from ThreatCrowd.", - "logo": "logos/threatcrowd.png", + "logo": "threatcrowd.png", "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512\n- whois-registrant-email", "output": "MISP attributes mapped from the result of the query on ThreatCrowd, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- hostname\n- whois-registrant-email", - "references": ["https://www.threatcrowd.org/"], + "references": [ + "https://www.threatcrowd.org/" + ], "features": "This module takes a MISP attribute as input and queries ThreatCrowd with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute." -} +} \ No newline at end of file diff --git a/doc/expansion/threatminer.json b/documentation/website/expansion/threatminer.json similarity index 87% rename from doc/expansion/threatminer.json rename to documentation/website/expansion/threatminer.json index d2f26bd..0b0d641 100644 --- a/doc/expansion/threatminer.json +++ b/documentation/website/expansion/threatminer.json @@ -1,8 +1,10 @@ { "description": "Module to get information from ThreatMiner.", - "logo": "logos/threatminer.png", + "logo": "threatminer.png", "input": "A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512", "output": "MISP attributes mapped from the result of the query on ThreatMiner, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- ssdeep\n- authentihash\n- filename\n- whois-registrant-email\n- url\n- link", - "references": ["https://www.threatminer.org/"], + "references": [ + "https://www.threatminer.org/" + ], "features": "This module takes a MISP attribute as input and queries ThreatMiner with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute." -} +} \ No newline at end of file diff --git a/doc/expansion/trustar_enrich.json b/documentation/website/expansion/trustar_enrich.json similarity index 81% rename from doc/expansion/trustar_enrich.json rename to documentation/website/expansion/trustar_enrich.json index 294419d..415f52d 100644 --- a/doc/expansion/trustar_enrich.json +++ b/documentation/website/expansion/trustar_enrich.json @@ -1,8 +1,10 @@ { "description": "Module to get enrich indicators with TruSTAR.", - "logo": "logos/trustar.png", + "logo": "trustar.png", "input": "Any of the following MISP attributes:\n- btc\n- domain\n- email-src\n- filename\n- hostname\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- url", "output": "MISP attributes enriched with indicator summary data from the TruSTAR API. Data includes a severity level score and additional source and scoring info.", - "references": ["https://docs.trustar.co/api/v13/indicators/get_indicator_summaries.html"], + "references": [ + "https://docs.trustar.co/api/v13/indicators/get_indicator_summaries.html" + ], "features": "This module enriches MISP attributes with scoring and metadata from TruSTAR.\n\nThe TruSTAR indicator summary is appended to the attributes along with links to any associated reports." -} +} \ No newline at end of file diff --git a/doc/expansion/urlhaus.json b/documentation/website/expansion/urlhaus.json similarity index 86% rename from doc/expansion/urlhaus.json rename to documentation/website/expansion/urlhaus.json index 8e5cef3..cd59661 100644 --- a/doc/expansion/urlhaus.json +++ b/documentation/website/expansion/urlhaus.json @@ -1,9 +1,11 @@ { "description": "Query of the URLhaus API to get additional information about the input attribute.", - "logo": "logos/urlhaus.png", + "logo": "urlhaus.png", "requirements": [], "input": "A domain, hostname, url, ip, md5 or sha256 attribute.", "output": "MISP attributes & objects fetched from the result of the URLhaus API query.", - "references": ["https://urlhaus.abuse.ch/"], + "references": [ + "https://urlhaus.abuse.ch/" + ], "features": "Module using the new format of modules able to return attributes and objects.\n\nThe module takes one of the attribute type specified as input, and query the URLhaus API with it. If any result is returned by the API, attributes and objects are created accordingly." -} +} \ No newline at end of file diff --git a/doc/expansion/urlscan.json b/documentation/website/expansion/urlscan.json similarity index 73% rename from doc/expansion/urlscan.json rename to documentation/website/expansion/urlscan.json index d847761..3aab2ab 100644 --- a/doc/expansion/urlscan.json +++ b/documentation/website/expansion/urlscan.json @@ -1,9 +1,13 @@ { "description": "An expansion module to query urlscan.io.", - "logo": "logos/urlscan.jpg", - "requirements": ["An access to the urlscan.io API"], + "logo": "urlscan.jpg", + "requirements": [ + "An access to the urlscan.io API" + ], "input": "A domain, hostname or url attribute.", "output": "MISP attributes mapped from the result of the query on urlscan.io.", - "references": ["https://urlscan.io/"], + "references": [ + "https://urlscan.io/" + ], "features": "This module takes a MISP attribute as input and queries urlscan.io with it.\n\nThe result of this query is then parsed and some data is mapped into MISP attributes in order to enrich the input attribute." -} +} \ No newline at end of file diff --git a/doc/expansion/virustotal.json b/documentation/website/expansion/virustotal.json similarity index 80% rename from doc/expansion/virustotal.json rename to documentation/website/expansion/virustotal.json index 31fd6ac..85c036f 100644 --- a/doc/expansion/virustotal.json +++ b/documentation/website/expansion/virustotal.json @@ -1,9 +1,14 @@ { "description": "Module to get advanced information from virustotal.", - "logo": "logos/virustotal.png", - "requirements": ["An access to the VirusTotal API (apikey), with a high request rate limit."], + "logo": "virustotal.png", + "requirements": [ + "An access to the VirusTotal API (apikey), with a high request rate limit." + ], "input": "A domain, hash (md5, sha1, sha256 or sha512), hostname or IP address attribute.", "output": "MISP attributes and objects resulting from the parsing of the VirusTotal report concerning the input attribute.", - "references": ["https://www.virustotal.com/", "https://developers.virustotal.com/reference"], + "references": [ + "https://www.virustotal.com/", + "https://developers.virustotal.com/reference" + ], "features": "New format of modules able to return attributes and objects.\n\nA module to take a MISP attribute as input and query the VirusTotal API to get additional data about it.\n\nCompared to the [standard VirusTotal expansion module](https://github.com/MISP/misp-modules/blob/master/misp_modules/modules/expansion/virustotal_public.py), this module is made for advanced parsing of VirusTotal report, with a recursive analysis of the elements found after the first request.\n\nThus, it requires a higher request rate limit to avoid the API to return a 204 error (Request rate limit exceeded), and the data parsed from the different requests are returned as MISP attributes and objects, with the corresponding relations between each one of them." -} +} \ No newline at end of file diff --git a/doc/expansion/virustotal_public.json b/documentation/website/expansion/virustotal_public.json similarity index 78% rename from doc/expansion/virustotal_public.json rename to documentation/website/expansion/virustotal_public.json index 242c734..2b9df12 100644 --- a/doc/expansion/virustotal_public.json +++ b/documentation/website/expansion/virustotal_public.json @@ -1,9 +1,14 @@ { "description": "Module to get information from VirusTotal.", - "logo": "logos/virustotal.png", - "requirements": ["An access to the VirusTotal API (apikey)"], + "logo": "virustotal.png", + "requirements": [ + "An access to the VirusTotal API (apikey)" + ], "input": "A domain, hostname, ip, url or hash (md5, sha1, sha256 or sha512) attribute.", "output": "MISP attributes and objects resulting from the parsing of the VirusTotal report concerning the input attribute.", - "references": ["https://www.virustotal.com", "https://developers.virustotal.com/reference"], + "references": [ + "https://www.virustotal.com", + "https://developers.virustotal.com/reference" + ], "features": "New format of modules able to return attributes and objects.\n\nA module to take a MISP attribute as input and query the VirusTotal API to get additional data about it.\n\nCompared to the [more advanced VirusTotal expansion module](https://github.com/MISP/misp-modules/blob/master/misp_modules/modules/expansion/virustotal.py), this module is made for VirusTotal users who have a low request rate limit.\n\nThus, it only queries the API once and returns the results that is parsed into MISP attributes and objects." -} +} \ No newline at end of file diff --git a/doc/expansion/vmray_submit.json b/documentation/website/expansion/vmray_submit.json similarity index 74% rename from doc/expansion/vmray_submit.json rename to documentation/website/expansion/vmray_submit.json index ea6cf3f..2b38792 100644 --- a/doc/expansion/vmray_submit.json +++ b/documentation/website/expansion/vmray_submit.json @@ -1,9 +1,13 @@ { "description": "Module to submit a sample to VMRay.", - "logo": "logos/vmray.png", - "requirements": ["An access to the VMRay API (apikey & url)"], + "logo": "vmray.png", + "requirements": [ + "An access to the VMRay API (apikey & url)" + ], "input": "An attachment or malware-sample attribute.", "output": "MISP attributes mapped from the result of the query on VMRay API, included in the following list:\n- text\n- sha1\n- sha256\n- md5\n- link", - "references": ["https://www.vmray.com/"], + "references": [ + "https://www.vmray.com/" + ], "features": "This module takes an attachment or malware-sample attribute as input to query the VMRay API.\n\nThe sample contained within the attribute in then enriched with data from VMRay mapped into MISP attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/vulndb.json b/documentation/website/expansion/vulndb.json similarity index 71% rename from doc/expansion/vulndb.json rename to documentation/website/expansion/vulndb.json index 330a3eb..e1dd869 100644 --- a/doc/expansion/vulndb.json +++ b/documentation/website/expansion/vulndb.json @@ -1,9 +1,13 @@ { "description": "Module to query VulnDB (RiskBasedSecurity.com).", - "logo": "logos/vulndb.png", - "requirements": ["An access to the VulnDB API (apikey, apisecret)"], + "logo": "vulndb.png", + "requirements": [ + "An access to the VulnDB API (apikey, apisecret)" + ], "input": "A vulnerability attribute.", "output": "Additional data enriching the CVE input, fetched from VulnDB.", - "references": ["https://vulndb.cyberriskanalytics.com/"], + "references": [ + "https://vulndb.cyberriskanalytics.com/" + ], "features": "This module takes a vulnerability attribute as input and queries VulnDB in order to get some additional data about it.\n\nThe API gives the result of the query which can be displayed in the screen, and/or mapped into MISP attributes to add in the event." -} +} \ No newline at end of file diff --git a/doc/expansion/vulners.json b/documentation/website/expansion/vulners.json similarity index 69% rename from doc/expansion/vulners.json rename to documentation/website/expansion/vulners.json index f3f3026..ab5a778 100644 --- a/doc/expansion/vulners.json +++ b/documentation/website/expansion/vulners.json @@ -1,9 +1,14 @@ { "description": "An expansion hover module to expand information about CVE id using Vulners API.", - "logo": "logos/vulners.png", - "requirements": ["Vulners python library", "An access to the Vulners API"], + "logo": "vulners.png", + "requirements": [ + "Vulners python library", + "An access to the Vulners API" + ], "input": "A vulnerability attribute.", "output": "Text giving additional information about the CVE in input.", - "references": ["https://vulners.com/"], + "references": [ + "https://vulners.com/" + ], "features": "This module takes a vulnerability attribute as input and queries the Vulners API in order to get some additional data about it.\n\nThe API then returns details about the vulnerability." -} +} \ No newline at end of file diff --git a/doc/expansion/whois.json b/documentation/website/expansion/whois.json similarity index 77% rename from doc/expansion/whois.json rename to documentation/website/expansion/whois.json index 938bad5..bba0828 100644 --- a/doc/expansion/whois.json +++ b/documentation/website/expansion/whois.json @@ -1,8 +1,12 @@ { "description": "Module to query a local instance of uwhois (https://github.com/rafiot/uwhoisd).", - "requirements": ["uwhois: A whois python library"], + "requirements": [ + "uwhois: A whois python library" + ], "input": "A domain or IP address attribute.", "output": "Text describing the result of a whois request for the input value.", - "references": ["https://github.com/rafiot/uwhoisd"], + "references": [ + "https://github.com/rafiot/uwhoisd" + ], "features": "This module takes a domain or IP address attribute as input and queries a 'Univseral Whois proxy server' to get the correct details of the Whois query on the input value (check the references for more details about this whois server)." -} +} \ No newline at end of file diff --git a/doc/expansion/wiki.json b/documentation/website/expansion/wiki.json similarity index 72% rename from doc/expansion/wiki.json rename to documentation/website/expansion/wiki.json index d6de62b..36bb009 100644 --- a/doc/expansion/wiki.json +++ b/documentation/website/expansion/wiki.json @@ -1,9 +1,13 @@ { "description": "An expansion hover module to extract information from Wikidata to have additional information about particular term for analysis.", - "logo": "logos/wikidata.png", - "requirements": ["SPARQLWrapper python library"], + "logo": "wikidata.png", + "requirements": [ + "SPARQLWrapper python library" + ], "input": "Text attribute.", "output": "Text attribute.", - "references": ["https://www.wikidata.org"], + "references": [ + "https://www.wikidata.org" + ], "features": "This module takes a text attribute as input and queries the Wikidata API. If the text attribute is clear enough to define a specific term, the API returns a wikidata link in response." -} +} \ No newline at end of file diff --git a/doc/expansion/xforceexchange.json b/documentation/website/expansion/xforceexchange.json similarity index 73% rename from doc/expansion/xforceexchange.json rename to documentation/website/expansion/xforceexchange.json index bbe3c86..fe6fcbb 100644 --- a/doc/expansion/xforceexchange.json +++ b/documentation/website/expansion/xforceexchange.json @@ -1,9 +1,13 @@ { "description": "An expansion module for IBM X-Force Exchange.", - "logo": "logos/xforce.png", - "requirements": ["An access to the X-Force API (apikey)"], + "logo": "xforce.png", + "requirements": [ + "An access to the X-Force API (apikey)" + ], "input": "A MISP attribute included in the following list:\n- ip-src\n- ip-dst\n- vulnerability\n- md5\n- sha1\n- sha256", "output": "MISP attributes mapped from the result of the query on X-Force Exchange.", - "references": ["https://exchange.xforce.ibmcloud.com/"], + "references": [ + "https://exchange.xforce.ibmcloud.com/" + ], "features": "This module takes a MISP attribute as input to query the X-Force API. The API returns then additional information known in their threats data, that is mapped into MISP attributes." -} +} \ No newline at end of file diff --git a/doc/expansion/xlsx_enrich.json b/documentation/website/expansion/xlsx_enrich.json similarity index 73% rename from doc/expansion/xlsx_enrich.json rename to documentation/website/expansion/xlsx_enrich.json index c41f17c..dff623d 100644 --- a/doc/expansion/xlsx_enrich.json +++ b/documentation/website/expansion/xlsx_enrich.json @@ -1,9 +1,11 @@ { "description": "Module to extract freetext from a .xlsx document.", - "logo": "logos/xlsx.png", - "requirements": ["pandas: Python library to perform data analysis, time series and statistics."], + "logo": "xlsx.png", + "requirements": [ + "pandas: Python library to perform data analysis, time series and statistics." + ], "input": "Attachment attribute containing a .xlsx document.", "output": "Text and freetext parsed from the document.", "references": [], "features": "The module reads the text contained in a .xlsx document. The result is passed to the freetext import parser so IoCs can be extracted out of it." -} +} \ No newline at end of file diff --git a/doc/expansion/yara_query.json b/documentation/website/expansion/yara_query.json similarity index 77% rename from doc/expansion/yara_query.json rename to documentation/website/expansion/yara_query.json index 408353d..453e599 100644 --- a/doc/expansion/yara_query.json +++ b/documentation/website/expansion/yara_query.json @@ -1,9 +1,14 @@ { "description": "An expansion & hover module to translate any hash attribute into a yara rule.", - "logo": "logos/yara.png", - "requirements": ["yara-python python library"], + "logo": "yara.png", + "requirements": [ + "yara-python python library" + ], "features": "The module takes a hash attribute (md5, sha1, sha256, imphash) as input, and is returning a YARA rule from it. This YARA rule is also validated using the same method as in 'yara_syntax_validator' module.\nBoth hover and expansion functionalities are supported with this module, where the hover part is displaying the resulting YARA rule and the expansion part allows you to add the rule as a new attribute, as usual with expansion modules.", "input": "MISP Hash attribute (md5, sha1, sha256, imphash, or any of the composite attribute with filename and one of the previous hash type).", "output": "YARA rule.", - "references": ["https://virustotal.github.io/yara/", "https://github.com/virustotal/yara-python"] -} + "references": [ + "https://virustotal.github.io/yara/", + "https://github.com/virustotal/yara-python" + ] +} \ No newline at end of file diff --git a/doc/expansion/yara_syntax_validator.json b/documentation/website/expansion/yara_syntax_validator.json similarity index 70% rename from doc/expansion/yara_syntax_validator.json rename to documentation/website/expansion/yara_syntax_validator.json index 93a96ee..72550b2 100644 --- a/doc/expansion/yara_syntax_validator.json +++ b/documentation/website/expansion/yara_syntax_validator.json @@ -1,9 +1,13 @@ { "description": "An expansion hover module to perform a syntax check on if yara rules are valid or not.", - "logo": "logos/yara.png", - "requirements": ["yara_python python library"], + "logo": "yara.png", + "requirements": [ + "yara_python python library" + ], "input": "YARA rule attribute.", "output": "Text to inform users if their rule is valid.", - "references": ["http://virustotal.github.io/yara/"], + "references": [ + "http://virustotal.github.io/yara/" + ], "features": "This modules simply takes a YARA rule as input, and checks its syntax. It returns then a confirmation if the syntax is valid, otherwise the syntax error is displayed." -} +} \ No newline at end of file diff --git a/documentation/website/export_mod/cef_export.json b/documentation/website/export_mod/cef_export.json new file mode 100644 index 0000000..cd247a7 --- /dev/null +++ b/documentation/website/export_mod/cef_export.json @@ -0,0 +1,10 @@ +{ + "description": "Module to export a MISP event in CEF format.", + "requirements": [], + "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in Common Event Format.\nThus, there is no particular feature concerning MISP Events since any event can be exported. However, 4 configuration parameters recognized by CEF format are required and should be provided by users before exporting data: the device vendor, product and version, as well as the default severity of data.", + "references": [ + "https://community.softwaregrp.com/t5/ArcSight-Connectors/ArcSight-Common-Event-Format-CEF-Guide/ta-p/1589306?attachment-id=65537" + ], + "input": "MISP Event attributes", + "output": "Common Event Format file" +} \ No newline at end of file diff --git a/doc/export_mod/cisco_firesight_manager_ACL_rule_export.json b/documentation/website/export_mod/cisco_firesight_manager_ACL_rule_export.json similarity index 79% rename from doc/export_mod/cisco_firesight_manager_ACL_rule_export.json rename to documentation/website/export_mod/cisco_firesight_manager_ACL_rule_export.json index 6d1d0dd..b9c72f9 100644 --- a/doc/export_mod/cisco_firesight_manager_ACL_rule_export.json +++ b/documentation/website/export_mod/cisco_firesight_manager_ACL_rule_export.json @@ -1,9 +1,11 @@ { "description": "Module to export malicious network activity attributes to Cisco fireSIGHT manager block rules.", - "logo": "logos/cisco.png", - "requirements": ["Firesight manager console credentials"], + "logo": "cisco.png", + "requirements": [ + "Firesight manager console credentials" + ], "input": "Network activity attributes (IPs, URLs).", "output": "Cisco fireSIGHT manager block rules.", "references": [], "features": "The module goes through the attributes to find all the network activity ones in order to create block rules for the Cisco fireSIGHT manager." -} +} \ No newline at end of file diff --git a/documentation/website/export_mod/goamlexport.json b/documentation/website/export_mod/goamlexport.json new file mode 100644 index 0000000..aaab295 --- /dev/null +++ b/documentation/website/export_mod/goamlexport.json @@ -0,0 +1,14 @@ +{ + "description": "This module is used to export MISP events containing transaction objects into GoAML format.", + "logo": "goAML.jpg", + "requirements": [ + "PyMISP", + "MISP objects" + ], + "features": "The module works as long as there is at least one transaction object in the Event.\n\nThen in order to have a valid GoAML document, please follow these guidelines:\n- For each transaction object, use either a bank-account, person, or legal-entity object to describe the origin of the transaction, and again one of them to describe the target of the transaction.\n- Create an object reference for both origin and target objects of the transaction.\n- A bank-account object needs a signatory, which is a person object, put as object reference of the bank-account.\n- A person can have an address, which is a geolocation object, put as object reference of the person.\n\nSupported relation types for object references that are recommended for each object are the folowing:\n- transaction:\n\t- 'from', 'from_my_client': Origin of the transaction - at least one of them is required.\n\t- 'to', 'to_my_client': Target of the transaction - at least one of them is required.\n\t- 'address': Location of the transaction - optional.\n- bank-account:\n\t- 'signatory': Signatory of a bank-account - the reference from bank-account to a signatory is required, but the relation-type is optional at the moment since this reference will always describe a signatory.\n\t- 'entity': Entity owning the bank account - optional.\n- person:\n\t- 'address': Address of a person - optional.", + "references": [ + "http://goaml.unodc.org/" + ], + "input": "MISP objects (transaction, bank-account, person, legal-entity, geolocation), with references, describing financial transactions and their origin and target.", + "output": "GoAML format file, describing financial transactions, with their origin and target (bank accounts, persons or entities)." +} \ No newline at end of file diff --git a/documentation/website/export_mod/liteexport.json b/documentation/website/export_mod/liteexport.json new file mode 100644 index 0000000..1f91039 --- /dev/null +++ b/documentation/website/export_mod/liteexport.json @@ -0,0 +1,8 @@ +{ + "description": "Lite export of a MISP event.", + "requirements": [], + "features": "This module is simply producing a json MISP event format file, but exporting only Attributes from the Event. Thus, MISP Events exported with this module should have attributes that are not internal references, otherwise the resulting event would be empty.", + "references": [], + "input": "MISP Event attributes", + "output": "Lite MISP Event" +} \ No newline at end of file diff --git a/doc/export_mod/mass_eql_export.json b/documentation/website/export_mod/mass_eql_export.json similarity index 74% rename from doc/export_mod/mass_eql_export.json rename to documentation/website/export_mod/mass_eql_export.json index 5eadd23..30b12a9 100644 --- a/doc/export_mod/mass_eql_export.json +++ b/documentation/website/export_mod/mass_eql_export.json @@ -1,9 +1,11 @@ { "description": "Mass EQL query export for a MISP event.", - "logo": "logos/eql.png", + "logo": "eql.png", "requirements": [], "features": "This module produces EQL queries for all relevant attributes in a MISP event.", - "references": ["https://eql.readthedocs.io/en/latest/"], + "references": [ + "https://eql.readthedocs.io/en/latest/" + ], "input": "MISP Event attributes", "output": "Text file containing one or more EQL queries" - } +} \ No newline at end of file diff --git a/documentation/website/export_mod/nexthinkexport.json b/documentation/website/export_mod/nexthinkexport.json new file mode 100644 index 0000000..0c06f9e --- /dev/null +++ b/documentation/website/export_mod/nexthinkexport.json @@ -0,0 +1,11 @@ +{ + "description": "Nexthink NXQL query export module", + "requirements": [], + "features": "This module export an event as Nexthink NXQL queries that can then be used in your own python3 tool or from wget/powershell", + "references": [ + "https://doc.nexthink.com/Documentation/Nexthink/latest/APIAndIntegrations/IntroducingtheWebAPIV2" + ], + "input": "MISP Event attributes", + "output": "Nexthink NXQL queries", + "logo": "nexthink.svg" +} \ No newline at end of file diff --git a/documentation/website/export_mod/osqueryexport.json b/documentation/website/export_mod/osqueryexport.json new file mode 100644 index 0000000..5b563c0 --- /dev/null +++ b/documentation/website/export_mod/osqueryexport.json @@ -0,0 +1,9 @@ +{ + "description": "OSQuery export of a MISP event.", + "requirements": [], + "features": "This module export an event as osquery queries that can be used in packs or in fleet management solution like Kolide.", + "references": [], + "input": "MISP Event attributes", + "output": "osquery SQL queries", + "logo": "osquery.png" +} \ No newline at end of file diff --git a/documentation/website/export_mod/pdfexport.json b/documentation/website/export_mod/pdfexport.json new file mode 100644 index 0000000..b23c681 --- /dev/null +++ b/documentation/website/export_mod/pdfexport.json @@ -0,0 +1,13 @@ +{ + "description": "Simple export of a MISP event to PDF.", + "requirements": [ + "PyMISP", + "reportlab" + ], + "features": "The module takes care of the PDF file building, and work with any MISP Event. Except the requirement of reportlab, used to create the file, there is no special feature concerning the Event. Some parameters can be given through the config dict. 'MISP_base_url_for_dynamic_link' is your MISP URL, to attach an hyperlink to your event on your MISP instance from the PDF. Keep it clear to avoid hyperlinks in the generated pdf.\n 'MISP_name_for_metadata' is your CERT or MISP instance name. Used as text in the PDF' metadata\n 'Activate_textual_description' is a boolean (True or void) to activate the textual description/header abstract of an event\n 'Activate_galaxy_description' is a boolean (True or void) to activate the description of event related galaxies.\n 'Activate_related_events' is a boolean (True or void) to activate the description of related event. Be aware this might leak information on confidential events linked to the current event !\n 'Activate_internationalization_fonts' is a boolean (True or void) to activate Noto fonts instead of default fonts (Helvetica). This allows the support of CJK alphabet. Be sure to have followed the procedure to download Noto fonts (~70Mo) in the right place (/tools/pdf_fonts/Noto_TTF), to allow PyMisp to find and use them during PDF generation.\n 'Custom_fonts_path' is a text (path or void) to the TTF file of your choice, to create the PDF with it. Be aware the PDF won't support bold/italic/special style anymore with this option ", + "references": [ + "https://acrobat.adobe.com/us/en/acrobat/about-adobe-pdf.html" + ], + "input": "MISP Event", + "output": "MISP Event in a PDF file." +} \ No newline at end of file diff --git a/doc/export_mod/testexport.json b/documentation/website/export_mod/testexport.json similarity index 95% rename from doc/export_mod/testexport.json rename to documentation/website/export_mod/testexport.json index 213ea92..884ccbe 100644 --- a/doc/export_mod/testexport.json +++ b/documentation/website/export_mod/testexport.json @@ -1,3 +1,3 @@ { "description": "Skeleton export module." -} +} \ No newline at end of file diff --git a/documentation/website/export_mod/threatStream_misp_export.json b/documentation/website/export_mod/threatStream_misp_export.json new file mode 100644 index 0000000..b096f41 --- /dev/null +++ b/documentation/website/export_mod/threatStream_misp_export.json @@ -0,0 +1,14 @@ +{ + "description": "Module to export a structured CSV file for uploading to threatStream.", + "logo": "threatstream.png", + "requirements": [ + "csv" + ], + "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in a CSV format recognized by ThreatStream.", + "references": [ + "https://www.anomali.com/platform/threatstream", + "https://github.com/threatstream" + ], + "input": "MISP Event attributes", + "output": "ThreatStream CSV format file" +} \ No newline at end of file diff --git a/documentation/website/export_mod/threat_connect_export.json b/documentation/website/export_mod/threat_connect_export.json new file mode 100644 index 0000000..23708dd --- /dev/null +++ b/documentation/website/export_mod/threat_connect_export.json @@ -0,0 +1,13 @@ +{ + "description": "Module to export a structured CSV file for uploading to ThreatConnect.", + "logo": "threatconnect.png", + "requirements": [ + "csv" + ], + "features": "The module takes a MISP event in input, to look every attribute. Each attribute matching with some predefined types is then exported in a CSV format recognized by ThreatConnect.\nUsers should then provide, as module configuration, the source of data they export, because it is required by the output format.", + "references": [ + "https://www.threatconnect.com" + ], + "input": "MISP Event attributes", + "output": "ThreatConnect CSV format file" +} \ No newline at end of file diff --git a/doc/export_mod/vt_graph.json b/documentation/website/export_mod/vt_graph.json similarity index 66% rename from doc/export_mod/vt_graph.json rename to documentation/website/export_mod/vt_graph.json index e317730..993c791 100644 --- a/doc/export_mod/vt_graph.json +++ b/documentation/website/export_mod/vt_graph.json @@ -1,9 +1,13 @@ { "description": "This module is used to create a VirusTotal Graph from a MISP event.", - "logo": "logos/virustotal.png", - "requirements": ["vt_graph_api, the python library to query the VirusTotal graph API"], + "logo": "virustotal.png", + "requirements": [ + "vt_graph_api, the python library to query the VirusTotal graph API" + ], "features": "The module takes the MISP event as input and queries the VirusTotal Graph API to create a new graph out of the event.\n\nOnce the graph is ready, we get the url of it, which is returned so we can view it on VirusTotal.", - "references": ["https://www.virustotal.com/gui/graph-overview"], + "references": [ + "https://www.virustotal.com/gui/graph-overview" + ], "input": "A MISP event.", "output": "Link of the VirusTotal Graph created for the event." -} +} \ No newline at end of file diff --git a/documentation/website/import_mod/csvimport.json b/documentation/website/import_mod/csvimport.json new file mode 100644 index 0000000..61bc6cc --- /dev/null +++ b/documentation/website/import_mod/csvimport.json @@ -0,0 +1,13 @@ +{ + "description": "Module to import MISP attributes from a csv file.", + "requirements": [ + "PyMISP" + ], + "features": "In order to parse data from a csv file, a header is required to let the module know which column is matching with known attribute fields / MISP types.\n\nThis header either comes from the csv file itself or is part of the configuration of the module and should be filled out in MISP plugin settings, each field separated by COMMAS. Fields that do not match with any type known in MISP or are not MISP attribute fields should be ignored in import, using a space or simply nothing between two separators (example: 'ip-src, , comment, ').\n\nIf the csv file already contains a header that does not start by a '#', you should tick the checkbox 'has_header' to avoid importing it and have potential issues. You can also redefine the header even if it is already contained in the file, by following the rules for headers explained earlier. One reason why you would redefine a header is for instance when you want to skip some fields, or some fields are not valid types.", + "references": [ + "https://tools.ietf.org/html/rfc4180", + "https://tools.ietf.org/html/rfc7111" + ], + "input": "CSV format file.", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/cuckooimport.json b/documentation/website/import_mod/cuckooimport.json new file mode 100644 index 0000000..2e51ea8 --- /dev/null +++ b/documentation/website/import_mod/cuckooimport.json @@ -0,0 +1,12 @@ +{ + "description": "Module to import Cuckoo JSON.", + "logo": "cuckoo.png", + "requirements": [], + "features": "The module simply imports MISP Attributes from a Cuckoo JSON format file. There is thus no special feature to make it work.", + "references": [ + "https://cuckoosandbox.org/", + "https://github.com/cuckoosandbox/cuckoo" + ], + "input": "Cuckoo JSON file", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/email_import.json b/documentation/website/import_mod/email_import.json new file mode 100644 index 0000000..95ec3c7 --- /dev/null +++ b/documentation/website/import_mod/email_import.json @@ -0,0 +1,8 @@ +{ + "description": "Module to import emails in MISP.", + "requirements": [], + "features": "This module can be used to import e-mail text as well as attachments and urls.\n3 configuration parameters are then used to unzip attachments, guess zip attachment passwords, and extract urls: set each one of them to True or False to process or not the respective corresponding actions.", + "references": [], + "input": "E-mail file", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/goamlimport.json b/documentation/website/import_mod/goamlimport.json new file mode 100644 index 0000000..e8f12cf --- /dev/null +++ b/documentation/website/import_mod/goamlimport.json @@ -0,0 +1,11 @@ +{ + "description": "Module to import MISP objects about financial transactions from GoAML files.", + "logo": "goAML.jpg", + "requirements": [ + "PyMISP" + ], + "features": "Unlike the GoAML export module, there is here no special feature to import data from GoAML external files, since the module will import MISP Objects with their References on its own, as it is required for the export module to rebuild a valid GoAML document.", + "references": "http://goaml.unodc.org/", + "input": "GoAML format file, describing financial transactions, with their origin and target (bank accounts, persons or entities).", + "output": "MISP objects (transaction, bank-account, person, legal-entity, geolocation), with references, describing financial transactions and their origin and target." +} \ No newline at end of file diff --git a/doc/import_mod/joe_import.json b/documentation/website/import_mod/joe_import.json similarity index 78% rename from doc/import_mod/joe_import.json rename to documentation/website/import_mod/joe_import.json index ceba4ab..f60d1dd 100644 --- a/doc/import_mod/joe_import.json +++ b/documentation/website/import_mod/joe_import.json @@ -1,9 +1,12 @@ { "description": "A module to import data from a Joe Sandbox analysis json report.", - "logo": "logos/joesandbox.png", + "logo": "joesandbox.png", "requirements": [], "input": "Json report of a Joe Sandbox analysis.", "output": "MISP attributes & objects parsed from the analysis report.", - "references": ["https://www.joesecurity.org", "https://www.joesandbox.com/"], - "features": "Module using the new format of modules able to return attributes and objects.\n\nThe module returns the same results as the expansion module [joesandbox_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) using the submission link of the analysis to get the json report.\n\n" + "references": [ + "https://www.joesecurity.org", + "https://www.joesandbox.com/" + ], + "features": "Module using the new format of modules able to return attributes and objects.\n\nThe module returns the same results as the expansion module [joesandbox_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/joesandbox_query.py) using the submission link of the analysis to get the json report." } diff --git a/doc/import_mod/lastline_import.json b/documentation/website/import_mod/lastline_import.json similarity index 86% rename from doc/import_mod/lastline_import.json rename to documentation/website/import_mod/lastline_import.json index 99414e0..d89a433 100644 --- a/doc/import_mod/lastline_import.json +++ b/documentation/website/import_mod/lastline_import.json @@ -1,9 +1,11 @@ { "description": "Module to import and parse reports from Lastline analysis links.", - "logo": "logos/lastline.png", + "logo": "lastline.png", "requirements": [], "input": "Link to a Lastline analysis.", "output": "MISP attributes and objects parsed from the analysis report.", - "references": ["https://www.lastline.com"], + "references": [ + "https://www.lastline.com" + ], "features": "The module requires a Lastline Portal `username` and `password`.\nThe module uses the new format and it is able to return MISP attributes and objects.\nThe module returns the same results as the [lastline_query](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/lastline_query.py) expansion module." -} +} \ No newline at end of file diff --git a/documentation/website/import_mod/mispjson.json b/documentation/website/import_mod/mispjson.json new file mode 100644 index 0000000..7ba47bd --- /dev/null +++ b/documentation/website/import_mod/mispjson.json @@ -0,0 +1,8 @@ +{ + "description": "Module to import MISP JSON format for merging MISP events.", + "requirements": [], + "features": "The module simply imports MISP Attributes from an other MISP Event in order to merge events together. There is thus no special feature to make it work.", + "references": [], + "input": "MISP Event", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/ocr.json b/documentation/website/import_mod/ocr.json new file mode 100644 index 0000000..a33c7e2 --- /dev/null +++ b/documentation/website/import_mod/ocr.json @@ -0,0 +1,8 @@ +{ + "description": "Optical Character Recognition (OCR) module for MISP.", + "requirements": [], + "features": "The module tries to recognize some text from an image and import the result as a freetext attribute, there is then no special feature asked to users to make it work.", + "references": [], + "input": "Image", + "output": "freetext MISP attribute" +} \ No newline at end of file diff --git a/documentation/website/import_mod/openiocimport.json b/documentation/website/import_mod/openiocimport.json new file mode 100644 index 0000000..3e00baf --- /dev/null +++ b/documentation/website/import_mod/openiocimport.json @@ -0,0 +1,12 @@ +{ + "description": "Module to import OpenIOC packages.", + "requirements": [ + "PyMISP" + ], + "features": "The module imports MISP Attributes from OpenIOC packages, there is then no special feature for users to make it work.", + "references": [ + "https://www.fireeye.com/blog/threat-research/2013/10/openioc-basics.html" + ], + "input": "OpenIOC packages", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/threatanalyzer_import.json b/documentation/website/import_mod/threatanalyzer_import.json new file mode 100644 index 0000000..5866e09 --- /dev/null +++ b/documentation/website/import_mod/threatanalyzer_import.json @@ -0,0 +1,10 @@ +{ + "description": "Module to import ThreatAnalyzer archive.zip / analysis.json files.", + "requirements": [], + "features": "The module imports MISP Attributes from a ThreatAnalyzer format file. This file can be either ZIP, or JSON format.\nThere is by the way no special feature for users to make the module work.", + "references": [ + "https://www.threattrack.com/malware-analysis.aspx" + ], + "input": "ThreatAnalyzer format file", + "output": "MISP Event attributes" +} \ No newline at end of file diff --git a/documentation/website/import_mod/vmray_import.json b/documentation/website/import_mod/vmray_import.json new file mode 100644 index 0000000..c80b237 --- /dev/null +++ b/documentation/website/import_mod/vmray_import.json @@ -0,0 +1,13 @@ +{ + "description": "Module to import VMRay (VTI) results.", + "logo": "vmray.png", + "requirements": [ + "vmray_rest_api" + ], + "features": "The module imports MISP Attributes from VMRay format, using the VMRay api.\nUsers should then provide as the module configuration the API Key as well as the server url in order to fetch their data to import.", + "references": [ + "https://www.vmray.com/" + ], + "input": "VMRay format", + "output": "MISP Event attributes" +} \ No newline at end of file From 6a83d171b999ba2eae82f9cf7ec47be77e0ef336 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Wed, 18 Nov 2020 11:43:33 +0100 Subject: [PATCH 220/220] chg: [pipenv] Updated lock Pipfile --- Pipfile.lock | 800 +++++++++++++++++++++++++++------------------------ 1 file changed, 427 insertions(+), 373 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index f862610..dbca0ce 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "41a685412700814c27cc8835a0a650a5f44dab172239cd06a7cb02f201a63e73" + "sha256": "1500257feb23545cff1594b4e16711ddb190a202858fcb95b469aa951a4b6b8c" }, "pipfile-spec": 6, "requires": { @@ -93,11 +93,11 @@ }, "attrs": { "hashes": [ - "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", - "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" + "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", + "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.2.0" + "version": "==20.3.0" }, "backscatter": { "hashes": [ @@ -125,10 +125,10 @@ }, "certifi": { "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd", + "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4" ], - "version": "==2020.6.20" + "version": "==2020.11.8" }, "cffi": { "hashes": [ @@ -380,42 +380,43 @@ }, "lxml": { "hashes": [ - "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b", - "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5", - "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301", - "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b", - "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d", - "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b", "sha256:24e811118aab6abe3ce23ff0d7d38932329c513f9cef849d3ee88b0f848f2aa9", - "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b", - "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311", - "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891", "sha256:302160eb6e9764168e01d8c9ec6becddeb87776e81d3fcb0d97954dd51d48e0a", - "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1", + "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd", + "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891", + "sha256:098fb713b31050463751dcc694878e1d39f316b86366fb9fe3fbbe5396ac9fab", + "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b", + "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360", "sha256:3d9b2b72eb0dbbdb0e276403873ecfae870599c83ba22cadff2db58541e72856", "sha256:475325e037fdf068e0c2140b818518cf6bc4aa72435c407a798b2db9f8e90810", - "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51", - "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360", - "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4", - "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f", - "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230", - "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a", - "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f", - "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174", - "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf", - "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd", - "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3", - "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a", - "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5", "sha256:c152b2e93b639d1f36ec5a8ca24cde4a8eefb2b6b83668fcd8e83a67badcb367", + "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a", + "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301", + "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3", + "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174", + "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f", + "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b", + "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a", "sha256:d182eada8ea0de61a45a526aa0ae4bcd222f9673424e65315c35820291ff299c", - "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1", + "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f", "sha256:d20d32cbb31d731def4b1502294ca2ee99f9249b63bc80e03e67e8f8e126dea8", - "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f", + "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1", + "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5", "sha256:d6f8c23f65a4bfe4300b85f1f40f6c32569822d08901db3b6454ab785d9117cc", - "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d", + "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f", + "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51", "sha256:e65c221b2115a91035b55a593b6eb94aa1206fa3ab374f47c6dc10d364583ff9", - "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f" + "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1", + "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4", + "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d", + "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b", + "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf", + "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5", + "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f", + "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230", + "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d", + "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b", + "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311" ], "index": "pypi", "version": "==4.6.1" @@ -449,42 +450,46 @@ }, "multidict": { "hashes": [ - "sha256:02b2ea2bb1277a970d238c5c783023790ca94d386c657aeeb165259950951cc6", - "sha256:0ce1d956ecbf112d49915ebc2f29c03e35fe451fb5e9f491edf9a2f4395ee0af", - "sha256:0ffdb4b897b15df798c0a5939a0323ccf703f2bae551dfab4eb1af7fbab38ead", - "sha256:11dcf2366da487d5b9de1d4b2055308c7ed9bde1a52973d07a89b42252af9ebe", - "sha256:167bd8e6351b57525bbf2d524ca5a133834699a2fcb090aad0c330c6017f3f3e", - "sha256:1b324444299c3a49b601b1bf621fc21704e29066f6ac2b7d7e4034a4a18662a1", - "sha256:20eaf1c279c543e07c164e4ac02151488829177da06607efa7ccfecd71b21e79", - "sha256:2739d1d9237835122b27d88990849ecf41ef670e0fcb876159edd236ca9ef40f", - "sha256:28b5913e5b6fef273e5d4230b61f33c8a51c3ce5f44a88582dee6b5ca5c9977b", - "sha256:2b0cfc33f53e5c8226f7d7c4e126fa0780f970ef1e96f7c6353da7d01eafe490", - "sha256:32f0a904859a6274d7edcbb01752c8ae9c633fb7d1c131771ff5afd32eceee42", - "sha256:39713fa2c687e0d0e709ad751a8a709ac051fcdc7f2048f6fd09365dd03c83eb", - "sha256:4ef76ce695da72e176f6a51867afb3bf300ce16ba2597824caaef625af5906a9", - "sha256:5263359a03368985b5296b7a73363d761a269848081879ba04a6e4bfd0cf4a78", - "sha256:52b5b51281d760197ce3db063c166fdb626e01c8e428a325aa37198ce31c9565", - "sha256:5dd303b545b62f9d2b14f99fbdb84c109a20e64a57f6a192fe6aebcb6263b59d", - "sha256:60af726c19a899ed49bbb276e062f08b80222cb6b9feda44b59a128b5ff52966", - "sha256:60b12d14bc122ba2dae1e4460a891b3a96e73d815b4365675f6ec0a1725416a5", - "sha256:620c39b1270b68e194023ad471b6a54bdb517bb48515939c9829b56c783504a3", - "sha256:62f6e66931fb87e9016e7c1cc806ab4f3e39392fd502362df3cac888078b27cb", - "sha256:711289412b78cf41a21457f4c806890466013d62bf4296bd3d71fad73ff8a581", - "sha256:7561a804093ea4c879e06b5d3d18a64a0bc21004bade3540a4b31342b528d326", - "sha256:786ad04ad954afe9927a1b3049aa58722e182160fe2fcac7ad7f35c93595d4f6", - "sha256:79dc3e6e7ce853fb7ed17c134e01fcb0d0c826b33201aa2a910fb27ed75c2eb9", - "sha256:84e4943d8725659942e7401bdf31780acde9cfdaf6fe977ff1449fffafcd93a9", - "sha256:932964cf57c0e59d1f3fb63ff342440cf8aaa75bf0dbcbad902c084024975380", - "sha256:a5eca9ee72b372199c2b76672145e47d3c829889eefa2037b1f3018f54e5f67d", - "sha256:aad240c1429e386af38a2d6761032f0bec5177fed7c5f582c835c99fff135b5c", - "sha256:bbec545b8f82536bc50afa9abce832176ed250aa22bfff3e20b3463fb90b0b35", - "sha256:c339b7d73c0ea5c551025617bb8aa1c00a0111187b6545f48836343e6cfbe6a0", - "sha256:c692087913e12b801a759e25a626c3d311f416252dfba2ecdfd254583427949f", - "sha256:cda06c99cd6f4a36571bb38e560a6fcfb1f136521e57f612e0bc31957b1cd4bd", - "sha256:ec8bc0ab00c76c4260a201eaa58812ea8b1b7fde0ecf5f9c9365a182bd4691ed" + "sha256:060d68ae3e674c913ec41a464916f12c4d7ff17a3a9ebbf37ba7f2c681c2b33e", + "sha256:06f39f0ddc308dab4e5fa282d145f90cd38d7ed75390fc83335636909a9ec191", + "sha256:17847fede1aafdb7e74e01bb34ab47a1a1ea726e8184c623c45d7e428d2d5d34", + "sha256:1cd102057b09223b919f9447c669cf2efabeefb42a42ae6233f25ffd7ee31a79", + "sha256:20cc9b2dd31761990abff7d0e63cd14dbfca4ebb52a77afc917b603473951a38", + "sha256:2576e30bbec004e863d87216bc34abe24962cc2e964613241a1c01c7681092ab", + "sha256:2ab9cad4c5ef5c41e1123ed1f89f555aabefb9391d4e01fd6182de970b7267ed", + "sha256:359ea00e1b53ceef282232308da9d9a3f60d645868a97f64df19485c7f9ef628", + "sha256:3e61cc244fd30bd9fdfae13bdd0c5ec65da51a86575ff1191255cae677045ffe", + "sha256:43c7a87d8c31913311a1ab24b138254a0ee89142983b327a2c2eab7a7d10fea9", + "sha256:4a3f19da871befa53b48dd81ee48542f519beffa13090dc135fffc18d8fe36db", + "sha256:4df708ef412fd9b59b7e6c77857e64c1f6b4c0116b751cb399384ec9a28baa66", + "sha256:59182e975b8c197d0146a003d0f0d5dc5487ce4899502061d8df585b0f51fba2", + "sha256:6128d2c0956fd60e39ec7d1c8f79426f0c915d36458df59ddd1f0cff0340305f", + "sha256:6168839491a533fa75f3f5d48acbb829475e6c7d9fa5c6e245153b5f79b986a3", + "sha256:62abab8088704121297d39c8f47156cb8fab1da731f513e59ba73946b22cf3d0", + "sha256:653b2bbb0bbf282c37279dd04f429947ac92713049e1efc615f68d4e64b1dbc2", + "sha256:6566749cd78cb37cbf8e8171b5cd2cbfc03c99f0891de12255cf17a11c07b1a3", + "sha256:76cbdb22f48de64811f9ce1dd4dee09665f84f32d6a26de249a50c1e90e244e0", + "sha256:8efcf070d60fd497db771429b1c769a3783e3a0dd96c78c027e676990176adc5", + "sha256:8fa4549f341a057feec4c3139056ba73e17ed03a506469f447797a51f85081b5", + "sha256:9380b3f2b00b23a4106ba9dd022df3e6e2e84e1788acdbdd27603b621b3288df", + "sha256:9ed9b280f7778ad6f71826b38a73c2fdca4077817c64bc1102fdada58e75c03c", + "sha256:a7b8b5bd16376c8ac2977748bd978a200326af5145d8d0e7f799e2b355d425b6", + "sha256:af271c2540d1cd2a137bef8d95a8052230aa1cda26dd3b2c73d858d89993d518", + "sha256:b561e76c9e21402d9a446cdae13398f9942388b9bff529f32dfa46220af54d00", + "sha256:b82400ef848bbac6b9035a105ac6acaa1fb3eea0d164e35bbb21619b88e49fed", + "sha256:b98af08d7bb37d3456a22f689819ea793e8d6961b9629322d7728c4039071641", + "sha256:c58e53e1c73109fdf4b759db9f2939325f510a8a5215135330fe6755921e4886", + "sha256:cbabfc12b401d074298bfda099c58dfa5348415ae2e4ec841290627cb7cb6b2e", + "sha256:d4a6fb98e9e9be3f7d70fd3e852369c00a027bd5ed0f3e8ade3821bcad257408", + "sha256:d99da85d6890267292065e654a329e1d2f483a5d2485e347383800e616a8c0b1", + "sha256:e58db0e0d60029915f7fc95a8683fa815e204f2e1990f1fb46a7778d57ca8c35", + "sha256:e5bf89fe57f702a046c7ec718fe330ed50efd4bcf74722940db2eb0919cddb1c", + "sha256:f612e8ef8408391a4a3366e3508bab8ef97b063b4918a317cb6e6de4415f01af", + "sha256:f65a2442c113afde52fb09f9a6276bbc31da71add99dc76c3adf6083234e07c6", + "sha256:fa0503947a99a1be94f799fac89d67a5e20c333e78ddae16e8534b151cdc588a" ], "markers": "python_version >= '3.5'", - "version": "==5.0.0" + "version": "==5.0.2" }, "np": { "hashes": [ @@ -495,43 +500,43 @@ }, "numpy": { "hashes": [ - "sha256:0ee77786eebbfa37f2141fd106b549d37c89207a0d01d8852fde1c82e9bfc0e7", - "sha256:199bebc296bd8a5fc31c16f256ac873dd4d5b4928dfd50e6c4995570fc71a8f3", - "sha256:1a307bdd3dd444b1d0daa356b5f4c7de2e24d63bdc33ea13ff718b8ec4c6a268", - "sha256:1ea7e859f16e72ab81ef20aae69216cfea870676347510da9244805ff9670170", - "sha256:271139653e8b7a046d11a78c0d33bafbddd5c443a5b9119618d0652a4eb3a09f", - "sha256:35bf5316af8dc7c7db1ad45bec603e5fb28671beb98ebd1d65e8059efcfd3b72", - "sha256:463792a249a81b9eb2b63676347f996d3f0082c2666fd0604f4180d2e5445996", - "sha256:50d3513469acf5b2c0406e822d3f314d7ac5788c2b438c24e5dd54d5a81ef522", - "sha256:50f68ebc439821b826823a8da6caa79cd080dee2a6d5ab9f1163465a060495ed", - "sha256:51e8d2ae7c7e985c7bebf218e56f72fa93c900ad0c8a7d9fbbbf362f45710f69", - "sha256:522053b731e11329dd52d258ddf7de5288cae7418b55e4b7d32f0b7e31787e9d", - "sha256:5ea4401ada0d3988c263df85feb33818dc995abc85b8125f6ccb762009e7bc68", - "sha256:604d2e5a31482a3ad2c88206efd43d6fcf666ada1f3188fd779b4917e49b7a98", - "sha256:6ff88bcf1872b79002569c63fe26cd2cda614e573c553c4d5b814fb5eb3d2822", - "sha256:7197ee0a25629ed782c7bd01871ee40702ffeef35bc48004bc2fdcc71e29ba9d", - "sha256:741d95eb2b505bb7a99fbf4be05fa69f466e240c2b4f2d3ddead4f1b5f82a5a5", - "sha256:83af653bb92d1e248ccf5fdb05ccc934c14b936bcfe9b917dc180d3f00250ac6", - "sha256:8802d23e4895e0c65e418abe67cdf518aa5cbb976d97f42fd591f921d6dffad0", - "sha256:8edc4d687a74d0a5f8b9b26532e860f4f85f56c400b3a98899fc44acb5e27add", - "sha256:942d2cdcb362739908c26ce8dd88db6e139d3fa829dd7452dd9ff02cba6b58b2", - "sha256:9a0669787ba8c9d3bb5de5d9429208882fb47764aa79123af25c5edc4f5966b9", - "sha256:9d08d84bb4128abb9fbd9f073e5c69f70e5dab991a9c42e5b4081ea5b01b5db0", - "sha256:9f7f56b5e85b08774939622b7d45a5d00ff511466522c44fc0756ac7692c00f2", - "sha256:a2daea1cba83210c620e359de2861316f49cc7aea8e9a6979d6cb2ddab6dda8c", - "sha256:b9074d062d30c2779d8af587924f178a539edde5285d961d2dfbecbac9c4c931", - "sha256:c4aa79993f5d856765819a3651117520e41ac3f89c3fc1cb6dee11aa562df6da", - "sha256:d78294f1c20f366cde8a75167f822538a7252b6e8b9d6dbfb3bdab34e7c1929e", - "sha256:dfdc8b53aa9838b9d44ed785431ca47aa3efaa51d0d5dd9c412ab5247151a7c4", - "sha256:dffed17848e8b968d8d3692604e61881aa6ef1f8074c99e81647ac84f6038535", - "sha256:e080087148fd70469aade2abfeadee194357defd759f9b59b349c6192aba994c", - "sha256:e983cbabe10a8989333684c98fdc5dd2f28b236216981e0c26ed359aaa676772", - "sha256:ea6171d2d8d648dee717457d0f75db49ad8c2f13100680e284d7becf3dc311a6", - "sha256:eefc13863bf01583a85e8c1121a901cc7cb8f059b960c4eba30901e2e6aba95f", - "sha256:efd656893171bbf1331beca4ec9f2e74358fc732a2084f664fd149cc4b3441d2" + "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db", + "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce", + "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1", + "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512", + "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2", + "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757", + "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9", + "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2", + "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08", + "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b", + "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb", + "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc", + "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac", + "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83", + "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36", + "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387", + "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f", + "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad", + "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c", + "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414", + "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37", + "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764", + "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753", + "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909", + "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6", + "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63", + "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9", + "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949", + "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab", + "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c", + "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3", + "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893", + "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15", + "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4" ], "markers": "python_version >= '3.6'", - "version": "==1.19.3" + "version": "==1.19.4" }, "oauth2": { "hashes": [ @@ -548,55 +553,60 @@ }, "opencv-python": { "hashes": [ - "sha256:16864152aa6ac346ef83588d6f4f5dc974d27851c034d6970fcb7b6a98bbd318", - "sha256:23dade76fe0194139112eea7ecdfa02ae09924b1d8d853f17f387a356519e484", - "sha256:27d5b83edd245a12dd6b8562569ad3f23e5ffe30cef8cfcc70756dd24b55d12f", - "sha256:2a2a7590b99d872b193cda0592b2c1cd6561159c31b361597c0e69e8926c8d16", - "sha256:46032d4648c74730115f8522effda8ac39bd0385f07edc7aab57b41cc7617933", - "sha256:4c195597d5286d1cc7259aeaeb7e6c1cde07fec9bddf26523eab1b15709291aa", - "sha256:69c971fefb633cfd334ed195d58e76e87f267649f98a2394f7400b178e918936", - "sha256:80b5b68e9c5dda29205ca112e6d5bd647b6b43cf917cfa5ce178d61675291bba", - "sha256:98676d349fdfc17dba9f23b87e9b6a639733d35f5f0ffcccb90e76c8200568f4", - "sha256:9df617736351100879b70d914366b9f9e38aa227885f2590b48badc4a233119d", - "sha256:b2147317b00b20e8d7e01201221af2b278aed449fa436316c42bc63f653e8245", - "sha256:d838ee4562f52793b1b10876e5067cae1a6bb1c3c575091644be9b88cf45d255", - "sha256:db74a92ef9c2a0810e1436d586b3b15d421a39b72f06263358f15c7a609498e0", - "sha256:e100a4ffdeed8c4afac6a5b3f6b4481efe0ad90e0a0ae2d129478abd4bd790bc", - "sha256:e87d88a820050c0e886c9add48eac2f80ff29207a98cca25869a6868c519daa4", - "sha256:fdf017c5b93d58ad77e2690e59322fd09414705c28d69b52fad4a19985422e6c" + "sha256:0548981fe189e0d57b9cc65066b66fd70d4bc84ea906f349a63d9098e1b911c6", + "sha256:117dbb2fd184de28d831f14c1da17864efcee7bb7895e43adf40f5e1da9137fb", + "sha256:135e05b69ab9665cbe2589f56e60895219bc2443a632bdc4bde72fb95eda1582", + "sha256:14df77490c8aedceae74e660564d48c04761658aecc93895ac5e974006a89606", + "sha256:17581c68400f828700e5c6b3b082f50c781bf74cb9a7b972a04f05d26c8e894a", + "sha256:4af0053c6a70f127a52c26b112341826d3dbfce6955beb9044d3eabd7e14d1cd", + "sha256:51baebb0f8f3cae4cccd30daf018a5bb75cb759d5658aea29100d34cd5cac106", + "sha256:6022609b67f9c0f14e6807e782660d1d1be94d4f0c7bc1794d7d8f600014acb2", + "sha256:68a9ec7e32f82cab267b6f757d9862a9a930371062739f9d00472e7c850c5854", + "sha256:6b1d85cbb64ce20ac5f79ad8e3e76a3dbff53d258c65f2fc0b9411321147a0be", + "sha256:6b6d23de6d5ddc55e865ac8532bf8062b26ba70305fa1c87c671717027dcd370", + "sha256:744e9ae2fb4c8574e6d4a762146b4d0984bdec60b98480fc54a363c03a07a1ac", + "sha256:7fe81d08df4eb5dc4c6aa5f09888b6fd390fce5fa7d5624a98cac890b9aa6181", + "sha256:8a8ebd7ceebc0be9c14ca3e25a1c4ae086016b469848258e998247f2fc855314", + "sha256:8aeda9b2c37bf91fa88d67f09b85f2250661eec43d72184ec544783de204e96a", + "sha256:9659e80059c9f39728c7dcc22032dff0d1d467f07b6cd8e036613393e4b7c71a", + "sha256:c1382209a771ca8a25fe89d4a2377875538c6ed3cf8745280e65636cbd0988f2", + "sha256:d80db278a07f51811dbf0f9c31ff7cd5b2501822fb7a7587e71f9ff27d5c04bd", + "sha256:db874c65654465ef71d6e8618bed8c725722bc90624132b9512bf061abb4eec0", + "sha256:e4c072cf4260063ebadc70e34d622fa1127a88e364475ed757709e249ebe990f", + "sha256:f69a56e958ecb549ba84e0497a438080932b4d52ded441cec04d80afde71dc0a" ], "index": "pypi", - "version": "==4.4.0.44" + "version": "==4.4.0.46" }, "pandas": { "hashes": [ - "sha256:ca71a5aa9eeb3ef5b31feca7d9b6369d6b3d0b2e9c85d7a89abe3ecb013f1e86", - "sha256:df43ea0e9fd9f9672b0de9cac26d01255ad50481994bf3cb4687c21eec2d7bbc", - "sha256:babbeda2f83b0686c9ad38d93b10516e68cdcd5771007eb80a763e98aaf44613", - "sha256:147162568b1242355290341baf281926cfac66ada07e634f3fc521ac967e4653", - "sha256:d6b1f9d506dc23da2915bcae5c5968990049c9cec44108bd9855d2c7c89d91dc", - "sha256:3a038cd5da602b955d335aa80cbaa0e5774f68501ff47b9c21509906981478da", - "sha256:84a4ffe668df357e31f98c829536e3a7142c3036c82f996e639f644c5d32eda1", - "sha256:882012763668af54b48f1412bab95c5cc0a7ccce5a2a8221cfc3839a6e3394ef", - "sha256:d89dbc58aec1544722a8d5046f880b597c497ef8a82c5fe695b4b2effafac5ec", - "sha256:5a8a84b75ca3a29bb4263b35d5ed9fcaae2b062f014feed8c5daa897339c7d85", - "sha256:24f61f40febe47edac271eda45d683e42838b7db2bd0f82574d9800259d2b182", - "sha256:b11b496c317dbe007898de699fd59eaf687d0fe8c1b7dad109db6010155d28ae", - "sha256:206d7c3e5356dcadf082e64dc25c24bc8541718045826074f96346e9d6d05a20", - "sha256:c22e40f1b4d162ca18eb6b2c572e63eef220dbc9cc3de0241cefb77972621bb7", - "sha256:ca31ac8578d48da354cf66a473d4d5ff99277ca71d321dc7ea4e6fad3c6bb0fd", - "sha256:a605054fbca71ed1d08bb2aef6f73c84a579bbac956bfe8f9718d5e84cb41248", - "sha256:f4cb8252ae71f093f4a6b847adf0bc9330f109c48f08363c2071f189f1c89c87", - "sha256:2999adc6736f8cb4c69d65a6e2b25a11bcb395da5b048342b8e4d6fe055e57ae", - "sha256:54f5f564058b0280d588c3758abde82e280702c440db5faf0c686b80336096f9", - "sha256:b026e913d88fad3a74eea8ed5a5f98e8823080ea02f8d9bb0ec19e92552daad6", - "sha256:11c284769f41e95f7d16a327eb555989c5f29418aad075fa80c97ef3aa8fb885", - "sha256:920d30fdff65a079f071db635d282b4f583c2b26f2b58d5dca218aac7c59974d", - "sha256:427be9938b2f79ab298de84f87693914cda238a27cf10580da96caf3dff64115", - "sha256:fd6f05b6101d0e76f3e5c26a47be5be7be96ed84ef3981dc1852e76898e73594" + "sha256:09e0503758ad61afe81c9069505f8cb8c1e36ea8cc1e6826a95823ef5b327daf", + "sha256:0a11a6290ef3667575cbd4785a1b62d658c25a2fd70a5adedba32e156a8f1773", + "sha256:0d9a38a59242a2f6298fff45d09768b78b6eb0c52af5919ea9e45965d7ba56d9", + "sha256:112c5ba0f9ea0f60b2cc38c25f87ca1d5ca10f71efbee8e0f1bee9cf584ed5d5", + "sha256:185cf8c8f38b169dbf7001e1a88c511f653fbb9dfa3e048f5e19c38049e991dc", + "sha256:3aa8e10768c730cc1b610aca688f588831fa70b65a26cb549fbb9f35049a05e0", + "sha256:41746d520f2b50409dffdba29a15c42caa7babae15616bcf80800d8cfcae3d3e", + "sha256:43cea38cbcadb900829858884f49745eb1f42f92609d368cabcc674b03e90efc", + "sha256:5378f58172bd63d8c16dd5d008d7dcdd55bf803fcdbe7da2dcb65dbbf322f05b", + "sha256:54404abb1cd3f89d01f1fb5350607815326790efb4789be60508f458cdd5ccbf", + "sha256:5dac3aeaac5feb1016e94bde851eb2012d1733a222b8afa788202b836c97dad5", + "sha256:5fdb2a61e477ce58d3f1fdf2470ee142d9f0dde4969032edaf0b8f1a9dafeaa2", + "sha256:6613c7815ee0b20222178ad32ec144061cb07e6a746970c9160af1ebe3ad43b4", + "sha256:6d2b5b58e7df46b2c010ec78d7fb9ab20abf1d306d0614d3432e7478993fbdb0", + "sha256:8a5d7e57b9df2c0a9a202840b2881bb1f7a648eba12dd2d919ac07a33a36a97f", + "sha256:8b4c2055ebd6e497e5ecc06efa5b8aa76f59d15233356eb10dad22a03b757805", + "sha256:a15653480e5b92ee376f8458197a58cca89a6e95d12cccb4c2d933df5cecc63f", + "sha256:a7d2547b601ecc9a53fd41561de49a43d2231728ad65c7713d6b616cd02ddbed", + "sha256:a979d0404b135c63954dea79e6246c45dd45371a88631cdbb4877d844e6de3b6", + "sha256:b1f8111635700de7ac350b639e7e452b06fc541a328cf6193cf8fc638804bab8", + "sha256:c5a3597880a7a29a31ebd39b73b2c824316ae63a05c3c8a5ce2aea3fc68afe35", + "sha256:c681e8fcc47a767bf868341d8f0d76923733cbdcabd6ec3a3560695c69f14a1e", + "sha256:cf135a08f306ebbcfea6da8bf775217613917be23e5074c69215b91e180caab4", + "sha256:e2b8557fe6d0a18db4d61c028c6af61bfed44ef90e419ed6fadbdc079eba141e" ], "index": "pypi", - "version": "==1.1.3" + "version": "==1.1.4" }, "pandas-ods-reader": { "hashes": [ @@ -624,41 +634,61 @@ }, "pillow": { "hashes": [ - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", - "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", - "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", - "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", + "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", + "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", + "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", + "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", + "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", + "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", + "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", + "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", + "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", + "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", + "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", + "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", + "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", + "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", + "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72", + "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", + "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", + "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792" ], "index": "pypi", "version": "==8.0.1" @@ -703,98 +733,85 @@ }, "pycryptodome": { "hashes": [ - "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba", - "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299", - "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4", - "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", - "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5", - "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b", - "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb", - "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", - "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", - "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", - "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2", - "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68", - "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2", - "sha256:663f8de2b3df2e744d6e1610506e0ea4e213bde906795953c1e82279c169f0a7", - "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739", - "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60", - "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94", - "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e", - "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0", - "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", - "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", - "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", - "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd", - "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23", - "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc", - "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149", - "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876", - "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982", - "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c", - "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6", - "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856", - "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8", - "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e", - "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a", - "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82", - "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1", - "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21", - "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f", - "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345", - "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a" + "sha256:19cb674df6c74a14b8b408aa30ba8a89bd1c01e23505100fb45f930fbf0ed0d9", + "sha256:1cfdb92dca388e27e732caa72a1cc624520fe93752a665c3b6cd8f1a91b34916", + "sha256:27397aee992af69d07502126561d851ba3845aa808f0e55c71ad0efa264dd7d4", + "sha256:28f75e58d02019a7edc7d4135203d2501dfc47256d175c72c9798f9a129a49a7", + "sha256:2a68df525b387201a43b27b879ce8c08948a430e883a756d6c9e3acdaa7d7bd8", + "sha256:411745c6dce4eff918906eebcde78771d44795d747e194462abb120d2e537cd9", + "sha256:46e96aeb8a9ca8b1edf9b1fd0af4bf6afcf3f1ca7fa35529f5d60b98f3e4e959", + "sha256:4ed27951b0a17afd287299e2206a339b5b6d12de9321e1a1575261ef9c4a851b", + "sha256:50826b49fbca348a61529693b0031cdb782c39060fb9dca5ac5dff858159dc5a", + "sha256:5598dc6c9dbfe882904e54584322893eff185b98960bbe2cdaaa20e8a437b6e5", + "sha256:5c3c4865730dfb0263f822b966d6d58429d8b1e560d1ddae37685fd9e7c63161", + "sha256:5f19e6ef750f677d924d9c7141f54bade3cd56695bbfd8a9ef15d0378557dfe4", + "sha256:60febcf5baf70c566d9d9351c47fbd8321da9a4edf2eff45c4c31c86164ca794", + "sha256:62c488a21c253dadc9f731a32f0ac61e4e436d81a1ea6f7d1d9146ed4d20d6bd", + "sha256:6d3baaf82681cfb1a842f1c8f77beac791ceedd99af911e4f5fabec32bae2259", + "sha256:6e4227849e4231a3f5b35ea5bdedf9a82b3883500e5624f00a19156e9a9ef861", + "sha256:6e89bb3826e6f84501e8e3b205c22595d0c5492c2f271cbb9ee1c48eb1866645", + "sha256:70d807d11d508433daf96244ec1c64e55039e8a35931fc5ea9eee94dbe3cb6b5", + "sha256:76b1a34d74bb2c91bce460cdc74d1347592045627a955e9a252554481c17c52f", + "sha256:7798e73225a699651888489fbb1dbc565e03a509942a8ce6194bbe6fb582a41f", + "sha256:834b790bbb6bd18956f625af4004d9c15eed12d5186d8e57851454ae76d52215", + "sha256:843e5f10ecdf9d307032b8b91afe9da1d6ed5bb89d0bbec5c8dcb4ba44008e11", + "sha256:8f9f84059039b672a5a705b3c5aa21747867bacc30a72e28bf0d147cc8ef85ed", + "sha256:9000877383e2189dafd1b2fc68c6c726eca9a3cfb6d68148fbb72ccf651959b6", + "sha256:910e202a557e1131b1c1b3f17a63914d57aac55cf9fb9b51644962841c3995c4", + "sha256:946399d15eccebafc8ce0257fc4caffe383c75e6b0633509bd011e357368306c", + "sha256:a199e9ca46fc6e999e5f47fce342af4b56c7de85fae893c69ab6aa17531fb1e1", + "sha256:a3d8a9efa213be8232c59cdc6b65600276508e375e0a119d710826248fd18d37", + "sha256:a4599c0ca0fc027c780c1c45ed996d5bef03e571470b7b1c7171ec1e1a90914c", + "sha256:b4e6b269a8ddaede774e5c3adbef6bf452ee144e6db8a716d23694953348cd86", + "sha256:b68794fba45bdb367eeb71249c26d23e61167510a1d0c3d6cf0f2f14636e62ee", + "sha256:d7ec2bd8f57c559dd24e71891c51c25266a8deb66fc5f02cc97c7fb593d1780a", + "sha256:e15bde67ccb7d4417f627dd16ffe2f5a4c2941ce5278444e884cb26d73ecbc61", + "sha256:eb01f9997e4d6a8ec8a1ad1f676ba5a362781ff64e8189fe2985258ba9cb9706", + "sha256:faa682c404c218e8788c3126c9a4b8fbcc54dc245b5b6e8ea5b46f3b63bd0c84" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.9.8" + "version": "==3.9.9" }, "pycryptodomex": { "hashes": [ - "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", - "sha256:93a75d1acd54efed314b82c952b39eac96ce98d241ad7431547442e5c56138aa", - "sha256:2199708ebeed4b82eb45b10e1754292677f5a0df7d627ee91ea01290b9bab7e6", - "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", - "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", - "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", - "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", - "sha256:1714675fb4ac29a26ced38ca22eb8ffd923ac851b7a6140563863194d7158422", - "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", - "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", - "sha256:4ae6379350a09339109e9b6f419bb2c3f03d3e441f4b0f5b8ca699d47cc9ff7e", - "sha256:9fd758e5e2fe02d57860b85da34a1a1e7037155c4eadc2326fc7af02f9cae214", - "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", - "sha256:2275a663c9e744ee4eace816ef2d446b3060554c5773a92fbc79b05bf47debda", - "sha256:2710fc8d83b3352b370db932b3710033b9d630b970ff5aaa3e7458b5336e3b32", - "sha256:35b9c9177a9fe7288b19dd41554c9c8ca1063deb426dd5a02e7e2a7416b6bd11", - "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", - "sha256:3caa32cf807422adf33c10c88c22e9e2e08b9d9d042f12e1e25fe23113dd618f", - "sha256:58e19560814dabf5d788b95a13f6b98279cf41a49b1e49ee6cf6c79a57adb4c9", - "sha256:8044eae59301dd392fbb4a7c5d64e1aea8ef0be2540549807ecbe703d6233d68", - "sha256:85c108b42e47d4073344ff61d4e019f1d95bb7725ca0fe87d0a2deb237c10e49", - "sha256:89be1bf55e50116fe7e493a7c0c483099770dd7f81b87ac8d04a43b1a203e259", - "sha256:8fcdda24dddf47f716400d54fc7f75cadaaba1dd47cc127e59d752c9c0fc3c48", - "sha256:914fbb18e29c54585e6aa39d300385f90d0fa3b3cc02ed829b08f95c1acf60c2", - "sha256:a2bc4e1a2e6ca3a18b2e0be6131a23af76fecb37990c159df6edc7da6df913e3", - "sha256:a2ee8ba99d33e1a434fcd27d7d0aa7964163efeee0730fe2efc9d60edae1fc71", - "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", - "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", - "sha256:c315262e26d54a9684e323e37ac9254f481d57fcc4fd94002992460898ef5c04", - "sha256:c990f2c58f7c67688e9e86e6557ed05952669ff6f1343e77b459007d85f7df00", - "sha256:ccbbec59bf4b74226170c54476da5780c9176bae084878fc94d9a2c841218e34", - "sha256:dc2bed32c7b138f1331794e454a953360c8cedf3ee62ae31f063822da6007489", - "sha256:ddb1ae2891c8cb83a25da87a3e00111a9654fc5f0b70f18879c41aece45d6182", - "sha256:e070a1f91202ed34c396be5ea842b886f6fa2b90d2db437dc9fb35a26c80c060", - "sha256:c0d085c8187a1e4d3402f626c9e438b5861151ab132d8761d9c5ce6491a87761", - "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb", - "sha256:3b23d63030819b7d9ac7db9360305fd1241e6870ca5b7e8d59fee4db4674a490", - "sha256:b2d756620078570d3f940c84bc94dd30aa362b795cce8b2723300a8800b87f1c", - "sha256:4e0b27697fa1621c6d3d3b4edeec723c2e841285de6a8d378c1962da77b349be", - "sha256:e4e1c486bf226822c8dceac81d0ec59c0a2399dbd1b9e04f03c3efa3605db677", - "sha256:ea4d4b58f9bc34e224ef4b4604a6be03d72ef1f8c486391f970205f6733dbc46", - "sha256:f5bd6891380e0fb5467251daf22525644fdf6afd9ae8bc2fe065c78ea1882e0d", - "sha256:f60b3484ce4be04f5da3777c51c5140d3fe21cdd6674f2b6568f41c8130bcdeb" + "sha256:15c03ffdac17731b126880622823d30d0a3cc7203cd219e6b9814140a44e7fab", + "sha256:20fb7f4efc494016eab1bc2f555bc0a12dd5ca61f35c95df8061818ffb2c20a3", + "sha256:28ee3bcb4d609aea3040cad995a8e2c9c6dc57c12183dadd69e53880c35333b9", + "sha256:305e3c46f20d019cd57543c255e7ba49e432e275d7c0de8913b6dbe57a851bc8", + "sha256:3547b87b16aad6afb28c9b3a9cd870e11b5e7b5ac649b74265258d96d8de1130", + "sha256:3642252d7bfc4403a42050e18ba748bedebd5a998a8cba89665a4f42aea4c380", + "sha256:404faa3e518f8bea516aae2aac47d4d960397199a15b4bd6f66cad97825469a0", + "sha256:42669638e4f7937b7141044a2fbd1019caca62bd2cdd8b535f731426ab07bde1", + "sha256:4632d55a140b28e20be3cd7a3057af52fb747298ff0fd3290d4e9f245b5004ba", + "sha256:4a88c9383d273bdce3afc216020282c9c5c39ec0bd9462b1a206af6afa377cf0", + "sha256:4ce1fc1e6d2fd2d6dc197607153327989a128c093e0e94dca63408f506622c3e", + "sha256:55cf4e99b3ba0122dee570dc7661b97bf35c16aab3e2ccb5070709d282a1c7ab", + "sha256:5e486cab2dfcfaec934dd4f5d5837f4a9428b690f4d92a3b020fd31d1497ca64", + "sha256:65ec88c8271448d2ea109d35c1f297b09b872c57214ab7e832e413090d3469a9", + "sha256:6c95a3361ce70068cf69526a58751f73ddac5ba27a3c2379b057efa2f5338c8c", + "sha256:73240335f4a1baf12880ebac6df66ab4d3a9212db9f3efe809c36a27280d16f8", + "sha256:7651211e15109ac0058a49159265d9f6e6423c8a81c65434d3c56d708417a05b", + "sha256:7b5b7c5896f8172ea0beb283f7f9428e0ab88ec248ce0a5b8c98d73e26267d51", + "sha256:836fe39282e75311ce4c38468be148f7fac0df3d461c5de58c5ff1ddb8966bac", + "sha256:871852044f55295449fbf225538c2c4118525093c32f0a6c43c91bed0452d7e3", + "sha256:892e93f3e7e10c751d6c17fa0dc422f7984cfd5eb6690011f9264dc73e2775fc", + "sha256:934e460c5058346c6f1d62fdf3db5680fbdfbfd212722d24d8277bf47cd9ebdc", + "sha256:9736f3f3e1761024200637a080a4f922f5298ad5d780e10dbb5634fe8c65b34c", + "sha256:a1d38a96da57e6103423a446079ead600b450cf0f8ebf56a231895abf77e7ffc", + "sha256:a385fceaa0cdb97f0098f1c1e9ec0b46cc09186ddf60ec23538e871b1dddb6dc", + "sha256:a7cf1c14e47027d9fb9d26aa62e5d603994227bd635e58a8df4b1d2d1b6a8ed7", + "sha256:a9aac1a30b00b5038d3d8e48248f3b58ea15c827b67325c0d18a447552e30fc8", + "sha256:b696876ee583d15310be57311e90e153a84b7913ac93e6b99675c0c9867926d0", + "sha256:bef9e9d39393dc7baec39ba4bac6c73826a4db02114cdeade2552a9d6afa16e2", + "sha256:c885fe4d5f26ce8ca20c97d02e88f5fdd92c01e1cc771ad0951b21e1641faf6d", + "sha256:d2d1388595cb5d27d9220d5cbaff4f37c6ec696a25882eb06d224d241e6e93fb", + "sha256:d2e853e0f9535e693fade97768cf7293f3febabecc5feb1e9b2ffdfe1044ab96", + "sha256:d62fbab185a6b01c5469eda9f0795f3d1a5bba24f5a5813f362e4b73a3c4dc70", + "sha256:f20a62397e09704049ce9007bea4f6bad965ba9336a760c6f4ef1b4192e12d6d", + "sha256:f81f7311250d9480e36dec819127897ae772e7e8de07abfabe931b8566770b8e" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==3.9.8" + "version": "==3.9.9" }, "pydeep": { "hashes": [ @@ -842,7 +859,7 @@ "pdfexport" ], "git": "https://github.com/MISP/PyMISP.git", - "ref": "deb9e06c726592c145e44b25fa6a05db56e3aa80" + "ref": "02eff91c1efaf9406164cd4d2ba0bc2036a9e67e" }, "pyonyphe": { "editable": true, @@ -1009,60 +1026,60 @@ }, "reportlab": { "hashes": [ - "sha256:00b9b3ffbd197b21cb076acc336993005b75d16b60f7a79a3c8faee926f890b7", - "sha256:0177b58d0ae81f6775b10e66f97bc7aa490659398e1f24401b6d1767803c4880", - "sha256:03c792a92ba21e75e05230ef1ce038025c23b124c706d7369dfa1475a0d24785", - "sha256:04044318273fa00487557f2e79bb6f8faa08185b8b1795cc29985ccb609c8680", - "sha256:217da82e7451e2b101a4bd72006a7e6c0d3203200cfb5c4d6a17b997b9ba73c6", - "sha256:226b5ef9af16aa8b3487513556ae7386239fe3ec8b121b1e23f45b850f0a10a8", - "sha256:24773aba8c74e1e023a1d3c3c60dbd6ef4a76472e38f13b5a214c8bb48db7aef", - "sha256:25eb9bb45e206b3a464f763d1231d70bb5f351c01d5ab94568e687fec4bd9eee", - "sha256:3d6d26294e8e3f6a639ee4a4b423d2cb0fa7de24c4cccea50a32d50d20db52ad", - "sha256:4987cca329df7f9bf4b6abea3e83c26a5a8edfe5b133344e24f146ddc8c09b9a", - "sha256:4b6a7e9a83e00cfe020c8e8bdd595384312228b24dcb40538d5cf00df15c5bff", - "sha256:531b70748dd89456c4e1d2132497bc8580ac74d7fcb790b8e2d1b20378655ba2", - "sha256:57abf06c045d16a85906fbdd8d826d7e334377bbb29b7442d249a95cf5f3a5c5", - "sha256:58877ed7390327bf4c41ca75473223866f7d8da0f8a606eb682127c8ac4af990", - "sha256:640d41838b1e663c5db53f3c32294cd742ac5cc4ba3098aeaad53297b7e1cc47", - "sha256:658471d5b06e121692449f44a4e39e3c7128fea757c4e9354b488f35ac3f82de", - "sha256:6f971a53e02682866886c451513143f46aed65704e46327bb6440604cd7cd7eb", - "sha256:78dcf1aff25ddf68b147e78b074bef1384e804dd54322eb1d1f1f680892f8788", - "sha256:793ed7edd50306cd05213ac012749dfe65768485bd493c3434936438d594a363", - "sha256:7a3512585308e5c73bf123457ccfc90acb99493df89fae6131caaec9ffe1e4ca", - "sha256:7ace84b3aae39b14ce7235d096bc81891f60b871b7edad2b656cb1729100e0f2", - "sha256:7e84d123ec98816fce5a97af2755d664519e7891e9793330ec271900acb2bfab", - "sha256:813c31d8b7f28ee2f38f238c3eb6afb02b81b00d749ab10e38b534843680aea7", - "sha256:8365efe779e43e8005eace19c11c36e6a4bbea86ddc868b8db122240391c1747", - "sha256:8412514dc0d1bf62c6b33a645b5a7c46933cc16f3678db5546d0ac4e27f3dbae", - "sha256:8d4ba2aea71ab6ec688b3f3416db0d457e7814a642433b7f407a3f29e054816d", - "sha256:99a7cdd8633a8717dd239917647b42d9a6b869a01c39019c7b0b08b963be2a7e", - "sha256:9d86fe83e9c4838e0048f14067869d1ca8722bb52545781db7a9d345939e77f0", - "sha256:a626a97ab135f2129d87c5f98b2aee45e0ef1652bc9afef92509a8f5a5f72e45", - "sha256:a921906c1deb199f7910163703e4073b52e8d7f00d56d4f6bbc255a6ca3cfb1d", - "sha256:b80840cc4fece1426d30070a9dad016d9589e8d82ebddfc9ed30004b44ba2803", - "sha256:c5318b4e23803c7c5f2b7384858b7b6be5faf51f63664c97f6bf8601cd248855", - "sha256:cd5546d840f639587f352d4c54ff35422cbeba81eb2c50d156cd733015ecc4b2", - "sha256:d445fc4ada6a24a90080f7379d169fba1072ba5a75179ce2f5c3280adf605b45", - "sha256:d56f150bb4b2d32596291aa98d3c6986721c5cf41b8f90346a84cee8b7fb35f2", - "sha256:d6e42636247e4c6d2db929b9db01d1af907f63aa74af8123cd699107df8a7b23", - "sha256:dcf732695b1325289a9a74b849179d8475db32a00803644a664c2172a603237e", - "sha256:e7b20927e5e11bad8bac5d5b6c286ce2cae2804073513aa67f20986bc4b3b4e0", - "sha256:f6295876665359790dcb7042a9221c60e1f89dee042f33414e3ce440772f7aa1", - "sha256:f8ec6637f56c293ac62c9a94daebb856c4ef9b97eae4cf7b4e518813e41c8c75" + "sha256:06be7f04a631f02cd0202f7dee0d3e61dc265223f4ff861525ed7784b5552540", + "sha256:0a788a537c48915eda083485b59ac40ac012fa7c43070069bde6eb5ea588313c", + "sha256:1a7a38810e79653d0ea8e61db4f0517ac2a0e76edd2497cf6d4969dd3be30030", + "sha256:22301773db730545b44d4c77d8f29baf5683ccabec9883d978e8b8eda6d2175f", + "sha256:2906321b3d2779faafe47e2c13f9c69e1fb4ddb907f5a49cab3f9b0ea95df1f5", + "sha256:2d65f9cc5c0d3f63b5d024e6cf92234f1ab1f267cc9e5a847ab5d3efe1c3cf3e", + "sha256:2e012f7b845ef9f1f5bd63461d5201fa624b019a65ff5a93d0002b4f915bbc89", + "sha256:31ccfdbf5bb5ec85f0397661085ce4c9e52537ca0d2bf4220259666a4dcc55c2", + "sha256:3e10bd20c8ada9f7e1113157aa73b8e0048f2624e74794b73799c3deb13d7a3f", + "sha256:440d5f86c2b822abdb7981d691a78bdcf56f4710174830283034235ab2af2969", + "sha256:4f307accda32c9f17015ed77c7424f904514e349dff063f78d2462d715963e53", + "sha256:59659ee8897950fd1acd41a9cc61f4afdfda52dc2bb69a1924ce68089491849d", + "sha256:6216b11313467989ac9d9578ea3756d0af46e97184ee4e11a6b7ef652458f70d", + "sha256:6268a9a3d75e714b22beeb7687270956b06b232ccfdf37b1c6462961eab04457", + "sha256:6b226830f80df066d5986a3fdb3eb4d1b6320048f3d9ade539a6c03a5bc8b3ec", + "sha256:6e10eba6a0e330096f4200b18824b3194c399329b7830e34baee1c04ea07f99f", + "sha256:6e224c16c3d6fafdb2fb67b33c4b84d984ec34869834b3a137809f2fe5b84778", + "sha256:7da162fa677b90bd14f19b20ff80fec18c24a31ac44e5342ba49e198b13c4f92", + "sha256:8406e960a974a65b765c9ff74b269aa64718b4af1e8c511ebdbd9a5b44b0c7e6", + "sha256:8999bb075102d1b8ca4aada6ca14653d52bf02e37fd064e477eb180741f75077", + "sha256:8f6163729612e815b89649aed2e237505362a78014199f819fd92f9e5c96769b", + "sha256:9699fa8f0911ad56b46cc60bbaebe1557fd1c9e8da98185a7a1c0c40193eba48", + "sha256:9a53d76eec33abda11617aad1c9f5f4a2d906dd2f92a03a3f1ea370efbb52c95", + "sha256:9ed4d761b726ff411565eddb10cb37a6bca0ec873d9a18a83cf078f4502a2d94", + "sha256:a020d308e7c2de284d5407e3c6c13e3977a62b314f7bfe19bcc69677931da589", + "sha256:a2e6c15aecbe631245aab639751a58671312cced7e17de1ed9c45fb37036f6c9", + "sha256:b10cb48606d97b70edb094576e3d493d40467395e4fc267655135a2c92defbe8", + "sha256:b8d6e9df5181ed07b7ae145258eb69e686133afc97930af51a3c0c9d784d834d", + "sha256:bbb297754f5cf25eb8fcb817752984252a7feb0ca83e383718e4eec2fb67ea32", + "sha256:be90599e5e78c1ddfcfee8c752108def58b4c672ebcc4d3d9aa7fe65e7d3f16b", + "sha256:bfdfad9b8ae00bd0752b77f954c7405327fd99b2cc6d5e4273e65be61429d56a", + "sha256:c1e5ef5089e16b249388f65d8c8f8b74989e72eb8332060dc580a2ecb967cfc2", + "sha256:c5ed342e29a5fd7eeb0f2ccf7e5b946b5f750f05633b2d6a94b1c02094a77967", + "sha256:c7087a26b26aa82a3ba27e13e66f507cc697f9ceb4c046c0f758876b55f040a5", + "sha256:cf589e980d92b0bf343fa512b9d3ae9ed0469cbffd99cb270b6c83da143cb437", + "sha256:e6fb762e524a4fb118be9f44dbd9456cf80e42253ee8f1bdb0ea5c1f882d4ba8", + "sha256:f2fde5abb6f21c1eff5430f380cdbbee7fdeda6af935a83730ddce9f0c4e504e", + "sha256:f585b3bf7062c228306acd7f40b2ad915b32603228c19bb225952cc98fd2015a", + "sha256:f955a6366cf8e6729776c96e281bede468acd74f6eb49a5bbb048646adaa43d8", + "sha256:fe882fd348d8429debbdac4518d6a42888a7f4ad613dc596ce94788169caeb08" ], "index": "pypi", - "version": "==3.5.54" + "version": "==3.5.55" }, "requests": { "extras": [ "security" ], "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", + "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" ], "index": "pypi", - "version": "==2.24.0" + "version": "==2.25.0" }, "requests-cache": { "hashes": [ @@ -1096,11 +1113,11 @@ }, "socialscan": { "hashes": [ - "sha256:688ef0ae170f3270f541c0fdfaa859bad1fe56955c692fdd3b312deed8a16362", - "sha256:6db737865f08f5a6745fb8ccd001ef13856b19b887da1c8dcd480544a3d47e92" + "sha256:3d0ca2b27d53fa4552312e07f60d3a3f513f7791a5f2bce16d3e0e3f295cd037", + "sha256:871cbc50f577b29f5f55d9c3ec5798d3abef31663f7cbe4d5c47bd5c380f6bae" ], "index": "pypi", - "version": "==1.3.0" + "version": "==1.4.1" }, "socketio-client": { "hashes": [ @@ -1144,26 +1161,58 @@ }, "tornado": { "hashes": [ - "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc", - "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52", - "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6", - "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d", - "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b", - "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673", - "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9", - "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a", - "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740" + "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb", + "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c", + "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288", + "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95", + "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558", + "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe", + "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791", + "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d", + "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326", + "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b", + "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4", + "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c", + "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910", + "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5", + "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c", + "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0", + "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675", + "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd", + "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f", + "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c", + "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea", + "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6", + "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05", + "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd", + "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575", + "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a", + "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37", + "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795", + "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f", + "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32", + "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c", + "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01", + "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4", + "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2", + "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921", + "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085", + "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df", + "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102", + "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5", + "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68", + "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5" ], "markers": "python_version >= '3.5'", - "version": "==6.0.4" + "version": "==6.1" }, "tqdm": { "hashes": [ - "sha256:2dd75fdb764f673b8187643496fcfbeac38348015b665878e582b152f3391cdb", - "sha256:93b7a6a9129fce904f6df4cf3ae7ff431d779be681a95c3344c26f3e6c09abfa" + "sha256:18d6a615aedd09ec8456d9524489dab330af4bd5c2a14a76eb3f9a0e14471afe", + "sha256:80d9d5165d678dbd027dd102dfb99f71bf05f333b61fb761dbba13b4ab719ead" ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.50.0" + "version": "==4.52.0" }, "trustar": { "hashes": [ @@ -1210,11 +1259,11 @@ }, "urllib3": { "hashes": [ - "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", - "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" + "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", + "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.11" + "version": "==1.26.2" }, "uwhois": { "editable": true, @@ -1300,59 +1349,63 @@ }, "yarl": { "hashes": [ - "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843", - "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037", - "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91", - "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805", - "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839", - "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd", - "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6", - "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb", - "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156", - "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6", - "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c", - "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150", - "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c", - "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7", - "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713", - "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019", - "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d", - "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2", - "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6", - "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2", - "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5", - "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb", - "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2", - "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af", - "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c", - "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862", - "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8", - "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a", - "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da", - "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c", - "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385", - "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c", - "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336" + "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e", + "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434", + "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366", + "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3", + "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec", + "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959", + "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e", + "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c", + "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6", + "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a", + "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6", + "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424", + "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e", + "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f", + "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50", + "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2", + "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc", + "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4", + "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970", + "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10", + "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0", + "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406", + "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896", + "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643", + "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721", + "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478", + "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724", + "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e", + "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8", + "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96", + "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25", + "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76", + "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2", + "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2", + "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c", + "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a", + "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71" ], "markers": "python_version >= '3.6'", - "version": "==1.6.2" + "version": "==1.6.3" } }, "develop": { "attrs": { "hashes": [ - "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594", - "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc" + "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", + "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.2.0" + "version": "==20.3.0" }, "certifi": { "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd", + "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4" ], - "version": "==2020.6.20" + "version": "==2020.11.8" }, "chardet": { "hashes": [ @@ -1510,11 +1563,11 @@ "security" ], "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", + "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" ], "index": "pypi", - "version": "==2.24.0" + "version": "==2.25.0" }, "six": { "hashes": [ @@ -1526,18 +1579,19 @@ }, "toml": { "hashes": [ - "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", - "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "version": "==0.10.1" + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" }, "urllib3": { "hashes": [ - "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", - "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" + "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", + "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.11" + "version": "==1.26.2" } } }