diff --git a/.gitignore b/.gitignore index 9d7ad0a..b4260a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.swp *.pem *.pyc +docs/build/ examples/keys.py examples/cudeso.py examples/feed-generator/output/*\.json diff --git a/README.md b/README.md index a171619..f435c6b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -**IMPORTANT NOTE**: This library will require **at least** python 3.6 starting the 1st of January 2020. If you have to legacy versions of python, please use PyMISP v2.4.119.1, and consider updating your system(s). Anything released within the last 2 years will do, starting with Ubuntu 18.04. +**IMPORTANT NOTE**: This library will require **at least** python 3.6 starting the 1st of January 2020. If you have legacy versions of python, please use PyMISP v2.4.119.1, and consider updating your system(s). Anything released within the last 2 years will do, starting with Ubuntu 18.04. -README -====== +# PyMISP - Python Library to access MISP [![Documentation Status](https://readthedocs.org/projects/pymisp/badge/?version=latest)](http://pymisp.readthedocs.io/?badge=latest) [![Build Status](https://travis-ci.org/MISP/PyMISP.svg?branch=main)](https://travis-ci.org/MISP/PyMISP) @@ -10,8 +9,6 @@ README [![PyPi version](https://img.shields.io/pypi/v/pymisp.svg)](https://pypi.python.org/pypi/pymisp/) [![Number of PyPI downloads](https://img.shields.io/pypi/dm/pymisp.svg)](https://pypi.python.org/pypi/pymisp/) -# PyMISP - Python Library to access MISP - PyMISP is a Python library to access [MISP](https://github.com/MISP/MISP) platforms via their REST API. PyMISP allows you to fetch events, add or update events/attributes, add or update samples or search for attributes. @@ -34,7 +31,7 @@ pip3 install pymisp[fileobjects,openioc,virustotal] ## Install the latest version from repo from development purposes -**Note**: poetry is required +**Note**: poetry is required; e.g., "pip3 install poetry" ``` git clone https://github.com/MISP/PyMISP.git && cd PyMISP @@ -83,7 +80,7 @@ python3 last.py -l 45m # 45 minutes ## Debugging -You have two options there: +You have two options here: 1. Pass `debug=True` to `PyMISP` and it will enable logging.DEBUG to stderr on the whole module @@ -94,7 +91,7 @@ You have two options there: import logging logger = logging.getLogger('pymisp') -# Configure it as you whish, for example, enable DEBUG mode: +# Configure it as you wish, for example, enable DEBUG mode: logger.setLevel(logging.DEBUG) ``` @@ -111,7 +108,7 @@ logging.basicConfig(level=logging.DEBUG, filename="debug.log", filemode='w', for ## Test cases 1. The content of `mispevent.py` is tested on every commit -2. The tests cases that require a running MISP instance can be run the following way: +2. The test cases that require a running MISP instance can be run the following way: ```bash @@ -133,13 +130,13 @@ A series of [Jupyter notebooks for PyMISP tutorial](https://github.com/MISP/PyMI ... or at least everything that can be imported/exported from/to a json blob -`AbstractMISP` is the master class, and inherit `collections.MutableMapping` which means +`AbstractMISP` is the master class, and inherits from `collections.MutableMapping` which means the class can be represented as a python dictionary. The abstraction assumes every property that should not be seen in the dictionary is prepended with a `_`, or its name is added to the private list `__not_jsonable` (accessible through `update_not_jsonable` and `set_not_jsonable`. -This master class has helpers that will make it easy to load, and export, to, and from, a json string. +This master class has helpers that make it easy to load, and export to, and from, a json string. `MISPEvent`, `MISPAttribute`, `MISPObjectReference`, `MISPObjectAttribute`, and `MISPObject` are subclasses of AbstractMISP, which mean that they can be handled as python dictionaries. @@ -148,6 +145,6 @@ are subclasses of AbstractMISP, which mean that they can be handled as python di Creating a new MISP object generator should be done using a pre-defined template and inherit `AbstractMISPObjectGenerator`. -Your new MISPObject generator need to generate attributes, and add them as class properties using `add_attribute`. +Your new MISPObject generator must generate attributes and add them as class properties using `add_attribute`. When the object is sent to MISP, all the class properties will be exported to the JSON export. diff --git a/docs/source/conf.py b/docs/source/conf.py index 5caa62b..c396fe6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -40,6 +40,7 @@ extensions = [ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'sphinx.ext.imgconverter', + 'recommonmark', ] napoleon_google_docstring = False @@ -132,6 +133,9 @@ pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True +# lief is a bit difficult to install +autodoc_mock_imports = ["lief"] + # -- Options for HTML output ---------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index f519501..886b516 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,7 +9,7 @@ Welcome to PyMISP's documentation! Contents: .. toctree:: - :maxdepth: 4 + :maxdepth: 2 README modules diff --git a/docs/source/modules.rst b/docs/source/modules.rst index 7db7414..1566ce4 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -1,5 +1,5 @@ -pymisp -====== +pymisp - Modules +================ .. toctree:: :maxdepth: 4 diff --git a/pymisp/api.py b/pymisp/api.py index 19e5579..794574f 100644 --- a/pymisp/api.py +++ b/pymisp/api.py @@ -90,13 +90,13 @@ class PyMISP: :param url: URL of the MISP instance you want to connect to :param key: API key of the user you want to use - :param ssl: can be True or False (to check or to not check the validity of the certificate. Or a CA_BUNDLE in case of self signed or other certificate (the concatenation of all the *.crt of the chain) + :param ssl: can be True or False (to check or to not check the validity of the certificate. Or a CA_BUNDLE in case of self signed or other certificate (the concatenation of all the crt of the chain) :param debug: Write all the debug information to stderr - :param proxies: Proxy dict as describes here: http://docs.python-requests.org/en/master/user/advanced/#proxies - :param cert: Client certificate, as described there: http://docs.python-requests.org/en/master/user/advanced/#client-side-certificates + :param proxies: Proxy dict, as described here: http://docs.python-requests.org/en/master/user/advanced/#proxies + :param cert: Client certificate, as described here: http://docs.python-requests.org/en/master/user/advanced/#client-side-certificates :param auth: The auth parameter is passed directly to requests, as described here: http://docs.python-requests.org/en/master/user/authentication/ :param tool: The software using PyMISP (string), used to set a unique user-agent - :param timeout: Timeout as described here: https://requests.readthedocs.io/en/master/user/advanced/#timeouts + :param timeout: Timeout, as described here: https://requests.readthedocs.io/en/master/user/advanced/#timeouts """ def __init__(self, url: str, key: str, ssl: bool = True, debug: bool = False, proxies: Mapping = {}, @@ -159,7 +159,8 @@ class PyMISP: def remote_acl(self, debug_type: str = 'findMissingFunctionNames') -> Dict: """This should return an empty list, unless the ACL is outdated. - debug_type can only be printAllFunctionNames, findMissingFunctionNames, or printRoleAccess + + :param debug_type: printAllFunctionNames, findMissingFunctionNames, or printRoleAccess """ response = self._prepare_request('GET', f'events/queryACL/{debug_type}') return self._check_json_response(response) @@ -222,13 +223,21 @@ class PyMISP: return self._check_json_response(response) def set_server_setting(self, setting: str, value: Union[str, int, bool], force: bool = False) -> Dict: - """Set a setting on the MISP instance""" + """Set a setting on the MISP instance + + :param setting: server setting name + :param value: value to set + :param force: override value test + """ data = {'value': value, 'force': force} response = self._prepare_request('POST', f'servers/serverSettingsEdit/{setting}', data=data) return self._check_json_response(response) def get_server_setting(self, setting: str) -> Dict: - """Get a setting from the MISP instance""" + """Get a setting from the MISP instance + + :param setting: server setting name + """ response = self._prepare_request('GET', f'servers/getSetting/{setting}') return self._check_json_response(response) @@ -254,7 +263,10 @@ class PyMISP: # ## BEGIN Event ## def events(self, pythonify: bool = False) -> Union[Dict, List[MISPEvent]]: - """Get all the events from the MISP instance""" + """Get all the events from the MISP instance + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'events/index') events_r = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in events_r: @@ -270,7 +282,13 @@ class PyMISP: deleted: Union[bool, int, list] = False, extended: Union[bool, int] = False, pythonify: bool = False) -> Union[Dict, MISPEvent]: - '''Get an event from a MISP instance''' + """Get an event from a MISP instance + + :param event: event to get + :param deleted: whether to include deleted events + :param extended: whether to get extended events + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ event_id = get_uuid_or_id_from_abstract_misp(event) data = {} if deleted: @@ -289,7 +307,11 @@ class PyMISP: return e def add_event(self, event: MISPEvent, pythonify: bool = False) -> Union[Dict, MISPEvent]: - '''Add a new event on a MISP instance''' + """Add a new event on a MISP instance + + :param event: event to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'events/add', data=event) new_event = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in new_event: @@ -299,7 +321,12 @@ class PyMISP: return e def update_event(self, event: MISPEvent, event_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPEvent]: - '''Update an event on a MISP instance''' + """Update an event on a MISP instance''' + + :param event: event to update + :param event_id: ID of event to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if event_id is None: eid = get_uuid_or_id_from_abstract_misp(event) else: @@ -313,14 +340,19 @@ class PyMISP: return e def delete_event(self, event: Union[MISPEvent, int, str, UUID]) -> Dict: - '''Delete an event from a MISP instance''' + """Delete an event from a MISP instance''' + + :param event: event to delete + """ event_id = get_uuid_or_id_from_abstract_misp(event) response = self._prepare_request('POST', f'events/delete/{event_id}') return self._check_json_response(response) def publish(self, event: Union[MISPEvent, int, str, UUID], alert: bool = False) -> Dict: - """Publish the event with one single HTTP POST. - The default is to not send a mail as it is assumed this method is called on update. + """Publish the event with one single HTTP POST + + :param event: event to publish + :param alert: whether to send an email. The default is to not send a mail as it is assumed this method is called on update. """ event_id = get_uuid_or_id_from_abstract_misp(event) if alert: @@ -330,7 +362,11 @@ class PyMISP: return self._check_json_response(response) def contact_event_reporter(self, event: Union[MISPEvent, int, str, UUID], message: str) -> Dict: - """Send a message to the reporter of an event""" + """Send a message to the reporter of an event + + :param event: event with reporter to contact + :param message: message to send + """ event_id = get_uuid_or_id_from_abstract_misp(event) to_post = {'message': message} response = self._prepare_request('POST', f'events/contact/{event_id}', data=to_post) @@ -341,7 +377,11 @@ class PyMISP: # ## BEGIN Object ### def get_object(self, misp_object: Union[MISPObject, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPObject]: - '''Get an object from the remote MISP instance''' + """Get an object from the remote MISP instance + + :param misp_object: object to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ object_id = get_uuid_or_id_from_abstract_misp(misp_object) r = self._prepare_request('GET', f'objects/view/{object_id}') misp_object_r = self._check_json_response(r) @@ -352,7 +392,12 @@ class PyMISP: return o def add_object(self, event: Union[MISPEvent, int, str, UUID], misp_object: MISPObject, pythonify: bool = False) -> Union[Dict, MISPObject]: - '''Add a MISP Object to an existing MISP event''' + """Add a MISP Object to an existing MISP event + + :param event: event to extend + :param misp_object: object to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ event_id = get_uuid_or_id_from_abstract_misp(event) r = self._prepare_request('POST', f'objects/add/{event_id}', data=misp_object) new_object = self._check_json_response(r) @@ -363,7 +408,12 @@ class PyMISP: return o def update_object(self, misp_object: MISPObject, object_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPObject]: - '''Update an object on a MISP instance''' + """Update an object on a MISP instance + + :param misp_object: object to update + :param object_id: ID of object to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if object_id is None: oid = get_uuid_or_id_from_abstract_misp(misp_object) else: @@ -377,13 +427,20 @@ class PyMISP: return o def delete_object(self, misp_object: Union[MISPObject, int, str, UUID]) -> Dict: - '''Delete an object from a MISP instance''' + """Delete an object from a MISP instance + + :param misp_object: object to delete + """ object_id = get_uuid_or_id_from_abstract_misp(misp_object) response = self._prepare_request('POST', f'objects/delete/{object_id}') return self._check_json_response(response) def add_object_reference(self, misp_object_reference: MISPObjectReference, pythonify: bool = False) -> Union[Dict, MISPObjectReference]: - """Add a reference to an object""" + """Add a reference to an object + + :param misp_object_reference: object reference + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'objectReferences/add', misp_object_reference) object_reference = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in object_reference: @@ -393,7 +450,10 @@ class PyMISP: return ref def delete_object_reference(self, object_reference: Union[MISPObjectReference, int, str, UUID]) -> Dict: - """Delete a reference to an object""" + """Delete a reference to an object + + :param object_reference: object reference + """ object_reference_id = get_uuid_or_id_from_abstract_misp(object_reference) response = self._prepare_request('POST', f'objectReferences/delete/{object_reference_id}') return self._check_json_response(response) @@ -401,7 +461,10 @@ class PyMISP: # Object templates def object_templates(self, pythonify: bool = False) -> Union[Dict, List[MISPObjectTemplate]]: - """Get all the object templates.""" + """Get all the object templates + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'objectTemplates/index') templates = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in templates: @@ -414,7 +477,11 @@ class PyMISP: return to_return def get_object_template(self, object_template: Union[MISPObjectTemplate, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPObjectTemplate]: - """Gets the full object template corresponting the UUID passed as parameter""" + """Gets the full object template + + :param object_template: template or ID to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ object_template_id = get_uuid_or_id_from_abstract_misp(object_template) r = self._prepare_request('GET', f'objectTemplates/view/{object_template_id}') object_template_r = self._check_json_response(r) @@ -434,7 +501,10 @@ class PyMISP: # ## BEGIN Attribute ### def attributes(self, pythonify: bool = False) -> Union[Dict, List[MISPAttribute]]: - """Get all the attributes from the MISP instance""" + """Get all the attributes from the MISP instance + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'attributes/index') attributes_r = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in attributes_r: @@ -447,7 +517,11 @@ class PyMISP: return to_return def get_attribute(self, attribute: Union[MISPAttribute, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPAttribute]: - '''Get an attribute from a MISP instance''' + """Get an attribute from a MISP instance + + :param attribute: attribute to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ attribute_id = get_uuid_or_id_from_abstract_misp(attribute) r = self._prepare_request('GET', f'attributes/view/{attribute_id}') attribute_r = self._check_json_response(r) @@ -458,9 +532,13 @@ class PyMISP: return a def add_attribute(self, event: Union[MISPEvent, int, str, UUID], attribute: MISPAttribute, pythonify: bool = False) -> Union[Dict, MISPAttribute, MISPShadowAttribute]: - '''Add an attribute to an existing MISP event - NOTE MISP 2.4.113+: you can pass a list of attributes. - In that case, the pythonified response is the following: {'attributes': [MISPAttribute], 'errors': {errors by attributes}}''' + """Add an attribute to an existing MISP event + + :param event: event to extend + :param attribute: attribute to add. NOTE MISP 2.4.113+: you can pass a list of attributes. + In that case, the pythonified response is the following: {'attributes': [MISPAttribute], 'errors': {errors by attributes}} + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ event_id = get_uuid_or_id_from_abstract_misp(event) r = self._prepare_request('POST', f'attributes/add/{event_id}', data=attribute) new_attribute = self._check_json_response(r) @@ -490,7 +568,12 @@ class PyMISP: return a def update_attribute(self, attribute: MISPAttribute, attribute_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPAttribute, MISPShadowAttribute]: - '''Update an attribute on a MISP instance''' + """Update an attribute on a MISP instance + + :param attribute: attribute to update + :param attribute_id: attribute ID to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if attribute_id is None: aid = get_uuid_or_id_from_abstract_misp(attribute) else: @@ -510,7 +593,11 @@ class PyMISP: return a def delete_attribute(self, attribute: Union[MISPAttribute, int, str, UUID], hard: bool = False) -> Dict: - '''Delete an attribute from a MISP instance''' + """Delete an attribute from a MISP instance + + :param attribute: attribute to delete + :param hard: flag for hard delete + """ attribute_id = get_uuid_or_id_from_abstract_misp(attribute) data = {} if hard: @@ -530,7 +617,11 @@ class PyMISP: # ## BEGIN Attribute Proposal ### def attribute_proposals(self, event: Optional[Union[MISPEvent, int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, List[MISPShadowAttribute]]: - """Get all the attribute proposals""" + """Get all the attribute proposals + + :param event: event + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ if event: event_id = get_uuid_or_id_from_abstract_misp(event) r = self._prepare_request('GET', f'shadowAttributes/index/{event_id}') @@ -547,7 +638,11 @@ class PyMISP: return to_return def get_attribute_proposal(self, proposal: Union[MISPShadowAttribute, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPShadowAttribute]: - """Get an attribute proposal""" + """Get an attribute proposal + + :param proposal: proposal to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ proposal_id = get_uuid_or_id_from_abstract_misp(proposal) r = self._prepare_request('GET', f'shadowAttributes/view/{proposal_id}') attribute_proposal = self._check_json_response(r) @@ -560,7 +655,12 @@ class PyMISP: # NOTE: the tree following method have a very specific meaning, look at the comments def add_attribute_proposal(self, event: Union[MISPEvent, int, str, UUID], attribute: MISPAttribute, pythonify: bool = False) -> Union[Dict, MISPShadowAttribute]: - '''Propose a new attribute in an event''' + """Propose a new attribute in an event + + :param event: event to receive new attribute + :param attribute: attribute to propose + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ event_id = get_uuid_or_id_from_abstract_misp(event) r = self._prepare_request('POST', f'shadowAttributes/add/{event_id}', data=attribute) new_attribute_proposal = self._check_json_response(r) @@ -571,7 +671,12 @@ class PyMISP: return a def update_attribute_proposal(self, initial_attribute: Union[MISPAttribute, int, str, UUID], attribute: MISPAttribute, pythonify: bool = False) -> Union[Dict, MISPShadowAttribute]: - '''Propose a change for an attribute''' + """Propose a change for an attribute + + :param initial_attribute: attribute to change + :param attribute: attribute to propose + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ initial_attribute_id = get_uuid_or_id_from_abstract_misp(initial_attribute) r = self._prepare_request('POST', f'shadowAttributes/edit/{initial_attribute_id}', data=attribute) update_attribute_proposal = self._check_json_response(r) @@ -582,21 +687,28 @@ class PyMISP: return a def delete_attribute_proposal(self, attribute: Union[MISPAttribute, int, str, UUID]) -> Dict: - '''Propose the deletion of an attribute''' + """Propose the deletion of an attribute + + :param attribute: attribute to delete + """ attribute_id = get_uuid_or_id_from_abstract_misp(attribute) response = self._prepare_request('POST', f'shadowAttributes/delete/{attribute_id}') return self._check_json_response(response) - # NOTE: You cannot modify an existing proposal, only accept/discard - def accept_attribute_proposal(self, proposal: Union[MISPShadowAttribute, int, str, UUID]) -> Dict: - '''Accept a proposal''' + """Accept a proposal. You cannot modify an existing proposal, only accept/discard + + :param proposal: attribute proposal to accept + """ proposal_id = get_uuid_or_id_from_abstract_misp(proposal) response = self._prepare_request('POST', f'shadowAttributes/accept/{proposal_id}') return self._check_json_response(response) def discard_attribute_proposal(self, proposal: Union[MISPShadowAttribute, int, str, UUID]) -> Dict: - '''Discard a proposal''' + """Discard a proposal. You cannot modify an existing proposal, only accept/discard + + :param proposal: attribute proposal to discard + """ proposal_id = get_uuid_or_id_from_abstract_misp(proposal) response = self._prepare_request('POST', f'shadowAttributes/discard/{proposal_id}') return self._check_json_response(response) @@ -608,7 +720,12 @@ class PyMISP: def sightings(self, misp_entity: Optional[AbstractMISP] = None, org: Optional[Union[MISPOrganisation, int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, List[MISPSighting]]: - """Get the list of sighting related to a MISPEvent or a MISPAttribute (depending on type of misp_entity)""" + """Get the list of sightings related to a MISPEvent or a MISPAttribute (depending on type of misp_entity) + + :param misp_entity: MISP entity + :param org: MISP organization + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ if isinstance(misp_entity, MISPEvent): url = 'sightings/listSightings' to_post = {'context': 'event', 'id': misp_entity.id} @@ -637,7 +754,12 @@ class PyMISP: def add_sighting(self, sighting: MISPSighting, attribute: Optional[Union[MISPAttribute, int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, MISPSighting]: - '''Add a new sighting (globally, or to a specific attribute)''' + """Add a new sighting (globally, or to a specific attribute) + + :param sighting: sighting to add + :param attribute: specific attribute to modify with the sighting + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if attribute: attribute_id = get_uuid_or_id_from_abstract_misp(attribute) r = self._prepare_request('POST', f'sightings/add/{attribute_id}', data=sighting) @@ -652,7 +774,10 @@ class PyMISP: return s def delete_sighting(self, sighting: Union[MISPSighting, int, str, UUID]) -> Dict: - '''Delete a sighting from a MISP instance''' + """Delete a sighting from a MISP instance + + :param sighting: sighting to delete + """ sighting_id = get_uuid_or_id_from_abstract_misp(sighting) response = self._prepare_request('POST', f'sightings/delete/{sighting_id}') return self._check_json_response(response) @@ -662,7 +787,10 @@ class PyMISP: # ## BEGIN Tags ### def tags(self, pythonify: bool = False) -> Union[Dict, List[MISPTag]]: - """Get the list of existing tags.""" + """Get the list of existing tags. + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'tags/index') tags = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in tags: @@ -675,7 +803,11 @@ class PyMISP: return to_return def get_tag(self, tag: Union[MISPTag, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPTag]: - """Get a tag by id.""" + """Get a tag by id. + + :param tag: tag to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ tag_id = get_uuid_or_id_from_abstract_misp(tag) r = self._prepare_request('GET', f'tags/view/{tag_id}') tag_r = self._check_json_response(r) @@ -686,11 +818,13 @@ class PyMISP: return t def add_tag(self, tag: MISPTag, pythonify: bool = False) -> Union[Dict, MISPTag]: - '''Add a new tag on a MISP instance - Notes: - * The user calling this method needs the Tag Editor permission - * It doesn't add a tag to an event, simply create it on a MISP instance. - ''' + """Add a new tag on a MISP instance. + The user calling this method needs the Tag Editor permission. + It doesn't add a tag to an event, simply creates it on the MISP instance. + + :param tag: tag to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'tags/add', data=tag) new_tag = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in new_tag: @@ -700,17 +834,30 @@ class PyMISP: return t def enable_tag(self, tag: MISPTag, pythonify: bool = False) -> Union[Dict, MISPTag]: - """Enable a tag.""" + """Enable a tag + + :param tag: tag to enable + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ tag.hide_tag = False return self.update_tag(tag, pythonify=pythonify) def disable_tag(self, tag: MISPTag, pythonify: bool = False) -> Union[Dict, MISPTag]: - """Disable a tag.""" + """Disable a tag + + :param tag: tag to disable + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ tag.hide_tag = True return self.update_tag(tag, pythonify=pythonify) def update_tag(self, tag: MISPTag, tag_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPTag]: - """Edit only the provided parameters of a tag.""" + """Edit only the provided parameters of a tag + + :param tag: tag to update + :aram tag_id: tag ID to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if tag_id is None: tid = get_uuid_or_id_from_abstract_misp(tag) else: @@ -724,7 +871,10 @@ class PyMISP: return t def delete_tag(self, tag: Union[MISPTag, int, str, UUID]) -> Dict: - '''Delete an attribute from a MISP instance''' + """Delete a tag from a MISP instance + + :param tag: tag to delete + """ tag_id = get_uuid_or_id_from_abstract_misp(tag) response = self._prepare_request('POST', f'tags/delete/{tag_id}') return self._check_json_response(response) @@ -734,7 +884,10 @@ class PyMISP: # ## BEGIN Taxonomies ### def taxonomies(self, pythonify: bool = False) -> Union[Dict, List[MISPTaxonomy]]: - """Get all the taxonomies.""" + """Get all the taxonomies + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'taxonomies/index') taxonomies = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in taxonomies: @@ -747,7 +900,11 @@ class PyMISP: return to_return def get_taxonomy(self, taxonomy: Union[MISPTaxonomy, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPTaxonomy]: - """Get a taxonomy from a MISP instance.""" + """Get a taxonomy by id from a MISP instance + + :param taxonomy: taxonomy to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ taxonomy_id = get_uuid_or_id_from_abstract_misp(taxonomy) r = self._prepare_request('GET', f'taxonomies/view/{taxonomy_id}') taxonomy_r = self._check_json_response(r) @@ -758,27 +915,38 @@ class PyMISP: return t def enable_taxonomy(self, taxonomy: Union[MISPTaxonomy, int, str, UUID]) -> Dict: - """Enable a taxonomy.""" + """Enable a taxonomy + + :param taxonomy: taxonomy to enable + """ taxonomy_id = get_uuid_or_id_from_abstract_misp(taxonomy) response = self._prepare_request('POST', f'taxonomies/enable/{taxonomy_id}') return self._check_json_response(response) def disable_taxonomy(self, taxonomy: Union[MISPTaxonomy, int, str, UUID]) -> Dict: - """Disable a taxonomy.""" + """Disable a taxonomy. + + :param taxonomy: taxonomy to disable + """ taxonomy_id = get_uuid_or_id_from_abstract_misp(taxonomy) self.disable_taxonomy_tags(taxonomy_id) response = self._prepare_request('POST', f'taxonomies/disable/{taxonomy_id}') return self._check_json_response(response) def disable_taxonomy_tags(self, taxonomy: Union[MISPTaxonomy, int, str, UUID]) -> Dict: - """Disable all the tags of a taxonomy.""" + """Disable all the tags of a taxonomy + + :param taxonomy: taxonomy with tags to disable + """ taxonomy_id = get_uuid_or_id_from_abstract_misp(taxonomy) response = self._prepare_request('POST', f'taxonomies/disableTag/{taxonomy_id}') return self._check_json_response(response) def enable_taxonomy_tags(self, taxonomy: Union[MISPTaxonomy, int, str, UUID]) -> Dict: - """Enable all the tags of a taxonomy. - NOTE: this automatically done when you call enable_taxonomy.""" + """Enable all the tags of a taxonomy. NOTE: this is automatically done when you call enable_taxonomy + + :param taxonomy: taxonomy with tags to enable + """ taxonomy_id = get_uuid_or_id_from_abstract_misp(taxonomy) t = self.get_taxonomy(taxonomy_id) if not t['Taxonomy']['enabled']: @@ -797,7 +965,10 @@ class PyMISP: # ## BEGIN Warninglists ### def warninglists(self, pythonify: bool = False) -> Union[Dict, List[MISPWarninglist]]: - """Get all the warninglists.""" + """Get all the warninglists. + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'warninglists/index') warninglists = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in warninglists: @@ -810,7 +981,11 @@ class PyMISP: return to_return def get_warninglist(self, warninglist: Union[MISPWarninglist, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPWarninglist]: - """Get a warninglist.""" + """Get a warninglist by id + + :param warninglist: warninglist to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ warninglist_id = get_uuid_or_id_from_abstract_misp(warninglist) r = self._prepare_request('GET', f'warninglists/view/{warninglist_id}') wl = self._check_json_response(r) @@ -821,9 +996,11 @@ class PyMISP: return w def toggle_warninglist(self, warninglist_id: Optional[Union[str, int, List[int]]] = None, warninglist_name: Optional[Union[str, List[str]]] = None, force_enable: bool = False) -> Dict: - '''Toggle (enable/disable) the status of a warninglist by ID. + '''Toggle (enable/disable) the status of a warninglist by id + :param warninglist_id: ID of the WarningList - :param force_enable: Force the warning list in the enabled state (does nothing is already enabled) + :param warninglist_name: name of the WarningList + :param force_enable: Force the warning list in the enabled state (does nothing if already enabled) ''' if warninglist_id is None and warninglist_name is None: raise PyMISPError('Either warninglist_id or warninglist_name is required.') @@ -844,17 +1021,26 @@ class PyMISP: return self._check_json_response(response) def enable_warninglist(self, warninglist: Union[MISPWarninglist, int, str, UUID]) -> Dict: - """Enable a warninglist.""" + """Enable a warninglist + + :param warninglist: warninglist to enable + """ warninglist_id = get_uuid_or_id_from_abstract_misp(warninglist) return self.toggle_warninglist(warninglist_id=warninglist_id, force_enable=True) def disable_warninglist(self, warninglist: Union[MISPWarninglist, int, str, UUID]) -> Dict: - """Disable a warninglist.""" + """Disable a warninglist + + :param warninglist: warninglist to disable + """ warninglist_id = get_uuid_or_id_from_abstract_misp(warninglist) return self.toggle_warninglist(warninglist_id=warninglist_id, force_enable=False) def values_in_warninglist(self, value: Iterator) -> Dict: - """Check if IOC values are in warninglist""" + """Check if IOC values are in warninglist + + :param value: iterator with values to check + """ response = self._prepare_request('POST', 'warninglists/checkValue', data=value) return self._check_json_response(response) @@ -868,7 +1054,10 @@ class PyMISP: # ## BEGIN Noticelist ### def noticelists(self, pythonify: bool = False) -> Union[Dict, List[MISPNoticelist]]: - """Get all the noticelists.""" + """Get all the noticelists + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'noticelists/index') noticelists = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in noticelists: @@ -881,7 +1070,11 @@ class PyMISP: return to_return def get_noticelist(self, noticelist: Union[MISPNoticelist, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPNoticelist]: - """Get a noticelist by id.""" + """Get a noticelist by id + + :param notistlist: Noticelist to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ noticelist_id = get_uuid_or_id_from_abstract_misp(noticelist) r = self._prepare_request('GET', f'noticelists/view/{noticelist_id}') noticelist_j = self._check_json_response(r) @@ -892,7 +1085,10 @@ class PyMISP: return n def enable_noticelist(self, noticelist: Union[MISPNoticelist, int, str, UUID]) -> Dict: - """Enable a noticelist by id.""" + """Enable a noticelist by id + + :param noticelist: Noticelist to enable + """ # FIXME: https://github.com/MISP/MISP/issues/4856 # response = self._prepare_request('POST', f'noticelists/enable/{noticelist_id}') noticelist_id = get_uuid_or_id_from_abstract_misp(noticelist) @@ -900,7 +1096,10 @@ class PyMISP: return self._check_json_response(response) def disable_noticelist(self, noticelist: Union[MISPNoticelist, int, str, UUID]) -> Dict: - """Disable a noticelist by id.""" + """Disable a noticelist by id + + :param noticelist: Noticelist to disable + """ # FIXME: https://github.com/MISP/MISP/issues/4856 # response = self._prepare_request('POST', f'noticelists/disable/{noticelist_id}') noticelist_id = get_uuid_or_id_from_abstract_misp(noticelist) @@ -917,7 +1116,10 @@ class PyMISP: # ## BEGIN Galaxy ### def galaxies(self, pythonify: bool = False) -> Union[Dict, List[MISPGalaxy]]: - """Get all the galaxies.""" + """Get all the galaxies + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'galaxies/index') galaxies = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in galaxies: @@ -930,7 +1132,11 @@ class PyMISP: return to_return def get_galaxy(self, galaxy: Union[MISPGalaxy, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPGalaxy]: - """Get a galaxy by id.""" + """Get a galaxy by id + + :param galaxy: galaxy to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ galaxy_id = get_uuid_or_id_from_abstract_misp(galaxy) r = self._prepare_request('GET', f'galaxies/view/{galaxy_id}') galaxy_j = self._check_json_response(r) @@ -950,7 +1156,10 @@ class PyMISP: # ## BEGIN Feed ### def feeds(self, pythonify: bool = False) -> Union[Dict, List[MISPFeed]]: - """Get the list of existing feeds.""" + """Get the list of existing feeds + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'feeds/index') feeds = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in feeds: @@ -963,7 +1172,11 @@ class PyMISP: return to_return def get_feed(self, feed: Union[MISPFeed, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPFeed]: - """Get a feed by id.""" + """Get a feed by id + + :param feed: feed to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ feed_id = get_uuid_or_id_from_abstract_misp(feed) r = self._prepare_request('GET', f'feeds/view/{feed_id}') feed_j = self._check_json_response(r) @@ -974,7 +1187,11 @@ class PyMISP: return f def add_feed(self, feed: MISPFeed, pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Add a new feed on a MISP instance''' + """Add a new feed on a MISP instance + + :param feed: feed to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ # FIXME: https://github.com/MISP/MISP/issues/4834 r = self._prepare_request('POST', 'feeds/add', data={'Feed': feed}) new_feed = self._check_json_response(r) @@ -985,7 +1202,11 @@ class PyMISP: return f def enable_feed(self, feed: Union[MISPFeed, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Enable a feed (fetching it will create event(s)''' + """Enable a feed; fetching it will create event(s) + + :param feed: feed to enable + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if not isinstance(feed, MISPFeed): feed_id = get_uuid_or_id_from_abstract_misp(feed) # In case we have a UUID f = MISPFeed() @@ -996,7 +1217,11 @@ class PyMISP: return self.update_feed(feed=f, pythonify=pythonify) def disable_feed(self, feed: Union[MISPFeed, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Disable a feed''' + """Disable a feed + + :param feed: feed to disable + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if not isinstance(feed, MISPFeed): feed_id = get_uuid_or_id_from_abstract_misp(feed) # In case we have a UUID f = MISPFeed() @@ -1007,7 +1232,11 @@ class PyMISP: return self.update_feed(feed=f, pythonify=pythonify) def enable_feed_cache(self, feed: Union[MISPFeed, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Enable the caching of a feed''' + """Enable the caching of a feed + + :param feed: feed to enable caching + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if not isinstance(feed, MISPFeed): feed_id = get_uuid_or_id_from_abstract_misp(feed) # In case we have a UUID f = MISPFeed() @@ -1018,7 +1247,11 @@ class PyMISP: return self.update_feed(feed=f, pythonify=pythonify) def disable_feed_cache(self, feed: Union[MISPFeed, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Disable the caching of a feed''' + """Disable the caching of a feed + + :param feed: feed to disable caching + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if not isinstance(feed, MISPFeed): feed_id = get_uuid_or_id_from_abstract_misp(feed) # In case we have a UUID f = MISPFeed() @@ -1029,7 +1262,12 @@ class PyMISP: return self.update_feed(feed=f, pythonify=pythonify) def update_feed(self, feed: MISPFeed, feed_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPFeed]: - '''Update a feed on a MISP instance''' + """Update a feed on a MISP instance + + :param feed: feed to update + :param feed_id: feed id + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if feed_id is None: fid = get_uuid_or_id_from_abstract_misp(feed) else: @@ -1044,13 +1282,19 @@ class PyMISP: return f def delete_feed(self, feed: Union[MISPFeed, int, str, UUID]) -> Dict: - '''Delete a feed from a MISP instance''' + """Delete a feed from a MISP instance + + :param feed: feed to delete + """ feed_id = get_uuid_or_id_from_abstract_misp(feed) response = self._prepare_request('POST', f'feeds/delete/{feed_id}') return self._check_json_response(response) def fetch_feed(self, feed: Union[MISPFeed, int, str, UUID]) -> Dict: - """Fetch one single feed""" + """Fetch one single feed by id + + :param feed: feed to fetch + """ feed_id = get_uuid_or_id_from_abstract_misp(feed) response = self._prepare_request('GET', f'feeds/fetchFromFeed/{feed_id}') return self._check_json_response(response) @@ -1061,7 +1305,10 @@ class PyMISP: return self._check_json_response(response) def cache_feed(self, feed: Union[MISPFeed, int, str, UUID]) -> Dict: - """Cache a specific feed""" + """Cache a specific feed by id + + :param feed: feed to cache + """ feed_id = get_uuid_or_id_from_abstract_misp(feed) response = self._prepare_request('GET', f'feeds/cacheFeeds/{feed_id}') return self._check_json_response(response) @@ -1091,7 +1338,10 @@ class PyMISP: # ## BEGIN Server ### def servers(self, pythonify: bool = False) -> Union[Dict, List[MISPServer]]: - """Get the existing servers the MISP instance can synchronise with""" + """Get the existing servers the MISP instance can synchronise with + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'servers/index') servers = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in servers: @@ -1104,7 +1354,11 @@ class PyMISP: return to_return def get_sync_config(self, pythonify: bool = False) -> Union[Dict, MISPServer]: - '''WARNING: This method only works if the user calling it is a sync user''' + """Get the sync server config. + WARNING: This method only works if the user calling it is a sync user + + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('GET', 'servers/createSync') server = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in server: @@ -1114,7 +1368,11 @@ class PyMISP: return s def import_server(self, server: MISPServer, pythonify: bool = False) -> Union[Dict, MISPServer]: - """Import a sync server config received from get_sync_config""" + """Import a sync server config received from get_sync_config + + :param server: sync server config + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'servers/import', data=server) server_j = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in server_j: @@ -1125,7 +1383,11 @@ class PyMISP: def add_server(self, server: MISPServer, pythonify: bool = False) -> Union[Dict, MISPServer]: """Add a server to synchronise with. - Note: You probably want to use ExpandedPyMISP.get_sync_config and ExpandedPyMISP.import_server instead""" + Note: You probably want to use ExpandedPyMISP.get_sync_config and ExpandedPyMISP.import_server instead + + :param server: sync server config + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'servers/add', data=server) server_j = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in server_j: @@ -1135,7 +1397,11 @@ class PyMISP: return s def update_server(self, server: MISPServer, server_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPServer]: - '''Update a server to synchronise with''' + """Update a server to synchronise with + + :param server: sync server config + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if server_id is None: sid = get_uuid_or_id_from_abstract_misp(server) else: @@ -1149,13 +1415,20 @@ class PyMISP: return s def delete_server(self, server: Union[MISPServer, int, str, UUID]) -> Dict: - '''Delete a sync server''' + """Delete a sync server + + :param server: sync server config + """ server_id = get_uuid_or_id_from_abstract_misp(server) response = self._prepare_request('POST', f'servers/delete/{server_id}') return self._check_json_response(response) def server_pull(self, server: Union[MISPServer, int, str, UUID], event: Optional[Union[MISPEvent, int, str, UUID]] = None) -> Dict: - '''Initialize a pull from a sync server''' + """Initialize a pull from a sync server, optionally limited to one event + + :param server: sync server config + :param event: event + """ server_id = get_uuid_or_id_from_abstract_misp(server) if event: event_id = get_uuid_or_id_from_abstract_misp(event) @@ -1167,7 +1440,11 @@ class PyMISP: return self._check_json_response(response) def server_push(self, server: Union[MISPServer, int, str, UUID], event: Optional[Union[MISPEvent, int, str, UUID]] = None) -> Dict: - '''Initialize a push to a sync server''' + """Initialize a push to a sync server, optionally limited to one event + + :param server: sync server config + :param event: event + """ server_id = get_uuid_or_id_from_abstract_misp(server) if event: event_id = get_uuid_or_id_from_abstract_misp(event) @@ -1179,7 +1456,10 @@ class PyMISP: return self._check_json_response(response) def test_server(self, server: Union[MISPServer, int, str, UUID]) -> Dict: - """Test if a sync link is working as expected""" + """Test if a sync link is working as expected + + :param server: sync server config + """ server_id = get_uuid_or_id_from_abstract_misp(server) response = self._prepare_request('POST', f'servers/testConnection/{server_id}') return self._check_json_response(response) @@ -1189,7 +1469,10 @@ class PyMISP: # ## BEGIN Sharing group ### def sharing_groups(self, pythonify: bool = False) -> Union[Dict, List[MISPSharingGroup]]: - """Get the existing sharing groups""" + """Get the existing sharing groups + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'sharingGroups/index') sharing_groups = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in sharing_groups: @@ -1202,7 +1485,11 @@ class PyMISP: return to_return def add_sharing_group(self, sharing_group: MISPSharingGroup, pythonify: bool = False) -> Union[Dict, MISPSharingGroup]: - """Add a new sharing group""" + """Add a new sharing group + + :param sharing_group: sharing group to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'sharingGroups/add', data=sharing_group) sharing_group_j = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in sharing_group_j: @@ -1212,7 +1499,10 @@ class PyMISP: return s def delete_sharing_group(self, sharing_group: Union[MISPSharingGroup, int, str, UUID]) -> Dict: - """Delete a sharing group""" + """Delete a sharing group + + :param sharing_group: sharing group to delete + """ sharing_group_id = get_uuid_or_id_from_abstract_misp(sharing_group) response = self._prepare_request('POST', f'sharingGroups/delete/{sharing_group_id}') return self._check_json_response(response) @@ -1220,9 +1510,10 @@ class PyMISP: def add_org_to_sharing_group(self, sharing_group: Union[MISPSharingGroup, int, str, UUID], organisation: Union[MISPOrganisation, int, str, UUID], extend: bool = False) -> Dict: '''Add an organisation to a sharing group. - :sharing_group: Sharing group's local instance ID, or Sharing group's global UUID - :organisation: Organisation's local instance ID, or Organisation's global UUID, or Organisation's name as known to the curent instance - :extend: Allow the organisation to extend the group + + :param sharing_group: Sharing group's local instance ID, or Sharing group's global UUID + :param organisation: Organisation's local instance ID, or Organisation's global UUID, or Organisation's name as known to the curent instance + :param extend: Allow the organisation to extend the group ''' sharing_group_id = get_uuid_or_id_from_abstract_misp(sharing_group) organisation_id = get_uuid_or_id_from_abstract_misp(organisation) @@ -1233,8 +1524,9 @@ class PyMISP: def remove_org_from_sharing_group(self, sharing_group: Union[MISPSharingGroup, int, str, UUID], organisation: Union[MISPOrganisation, int, str, UUID]) -> Dict: '''Remove an organisation from a sharing group. - :sharing_group: Sharing group's local instance ID, or Sharing group's global UUID - :organisation: Organisation's local instance ID, or Organisation's global UUID, or Organisation's name as known to the curent instance + + :param sharing_group: Sharing group's local instance ID, or Sharing group's global UUID + :param organisation: Organisation's local instance ID, or Organisation's global UUID, or Organisation's name as known to the curent instance ''' sharing_group_id = get_uuid_or_id_from_abstract_misp(sharing_group) organisation_id = get_uuid_or_id_from_abstract_misp(organisation) @@ -1245,9 +1537,10 @@ class PyMISP: def add_server_to_sharing_group(self, sharing_group: Union[MISPSharingGroup, int, str, UUID], server: Union[MISPServer, int, str, UUID], all_orgs: bool = False) -> Dict: '''Add a server to a sharing group. - :sharing_group: Sharing group's local instance ID, or Sharing group's global UUID - :server: Server's local instance ID, or URL of the Server, or Server's name as known to the curent instance - :all_orgs: Add all the organisations of the server to the group + + :param sharing_group: Sharing group's local instance ID, or Sharing group's global UUID + :param server: Server's local instance ID, or URL of the Server, or Server's name as known to the curent instance + :param all_orgs: Add all the organisations of the server to the group ''' sharing_group_id = get_uuid_or_id_from_abstract_misp(sharing_group) server_id = get_uuid_or_id_from_abstract_misp(server) @@ -1258,8 +1551,9 @@ class PyMISP: def remove_server_from_sharing_group(self, sharing_group: Union[MISPSharingGroup, int, str, UUID], server: Union[MISPServer, int, str, UUID]) -> Dict: '''Remove a server from a sharing group. - :sharing_group: Sharing group's local instance ID, or Sharing group's global UUID - :server: Server's local instance ID, or URL of the Server, or Server's name as known to the curent instance + + :param sharing_group: Sharing group's local instance ID, or Sharing group's global UUID + :param server: Server's local instance ID, or URL of the Server, or Server's name as known to the curent instance ''' sharing_group_id = get_uuid_or_id_from_abstract_misp(sharing_group) server_id = get_uuid_or_id_from_abstract_misp(server) @@ -1272,7 +1566,11 @@ class PyMISP: # ## BEGIN Organisation ### def organisations(self, scope="local", pythonify: bool = False) -> Union[Dict, List[MISPOrganisation]]: - """Get all the organisations.""" + """Get all the organisations + + :param scope: scope of organizations to get + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', f'organisations/index/scope:{scope}') organisations = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in organisations: @@ -1285,7 +1583,11 @@ class PyMISP: return to_return def get_organisation(self, organisation: Union[MISPOrganisation, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPOrganisation]: - '''Get an organisation.''' + """Get an organisation by id + + :param organisation: organization to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ organisation_id = get_uuid_or_id_from_abstract_misp(organisation) r = self._prepare_request('GET', f'organisations/view/{organisation_id}') organisation_j = self._check_json_response(r) @@ -1296,7 +1598,11 @@ class PyMISP: return o def add_organisation(self, organisation: MISPOrganisation, pythonify: bool = False) -> Union[Dict, MISPOrganisation]: - '''Add an organisation''' + """Add an organisation + + :param organisation: organization to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'admin/organisations/add', data=organisation) new_organisation = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in new_organisation: @@ -1306,7 +1612,12 @@ class PyMISP: return o def update_organisation(self, organisation: MISPOrganisation, organisation_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPOrganisation]: - '''Update an organisation''' + """Update an organisation + + :param organisation: organization to update + :param organisation_id: id to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if organisation_id is None: oid = get_uuid_or_id_from_abstract_misp(organisation) else: @@ -1320,7 +1631,10 @@ class PyMISP: return o def delete_organisation(self, organisation: Union[MISPOrganisation, int, str, UUID]) -> Dict: - '''Delete an organisation''' + """Delete an organisation by id + + :param organisation: organization to delete + """ # NOTE: MISP in inconsistent and currently require "delete" in the path and doesn't support HTTP DELETE organisation_id = get_uuid_or_id_from_abstract_misp(organisation) response = self._prepare_request('POST', f'admin/organisations/delete/{organisation_id}') @@ -1331,7 +1645,10 @@ class PyMISP: # ## BEGIN User ### def users(self, pythonify: bool = False) -> Union[Dict, List[MISPUser]]: - """Get all the users.""" + """Get all the users + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'admin/users/index') users = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in users: @@ -1344,8 +1661,12 @@ class PyMISP: return to_return def get_user(self, user: Union[MISPUser, int, str, UUID] = 'me', pythonify: bool = False, expanded: bool = False) -> Union[Dict, MISPUser, Tuple[MISPUser, MISPRole, List[MISPUserSetting]]]: - '''Get a user. `me` means the owner of the API key doing the query. - expanded also returns a MISPRole and a MISPUserSetting''' + """Get a user by id + + :param user: user to get; `me` means the owner of the API key doing the query + :param pythonify: Returns a PyMISP Object instead of the plain json output + :param expanded: Also returns a MISPRole and a MISPUserSetting + """ user_id = get_uuid_or_id_from_abstract_misp(user) r = self._prepare_request('GET', f'users/view/{user_id}') user_j = self._check_json_response(r) @@ -1367,7 +1688,11 @@ class PyMISP: return u, role, usersettings def add_user(self, user: MISPUser, pythonify: bool = False) -> Union[Dict, MISPUser]: - '''Add a new user''' + """Add a new user + + :param user: user to add + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ r = self._prepare_request('POST', 'admin/users/add', data=user) user_j = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in user_j: @@ -1377,7 +1702,12 @@ class PyMISP: return u def update_user(self, user: MISPUser, user_id: Optional[int] = None, pythonify: bool = False) -> Union[Dict, MISPUser]: - '''Update an event on a MISP instance''' + """Update a user on a MISP instance + + :param user: user to update + :param user_id: id to update + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if user_id is None: uid = get_uuid_or_id_from_abstract_misp(user) else: @@ -1394,19 +1724,28 @@ class PyMISP: return e def delete_user(self, user: Union[MISPUser, int, str, UUID]) -> Dict: - '''Delete a user''' + """Delete a user by id + + :param user: user to delete + """ # NOTE: MISP in inconsistent and currently require "delete" in the path and doesn't support HTTP DELETE user_id = get_uuid_or_id_from_abstract_misp(user) response = self._prepare_request('POST', f'admin/users/delete/{user_id}') return self._check_json_response(response) def change_user_password(self, new_password: str) -> Dict: - '''Thange the password of the curent user''' + """Change the password of the curent user + + :param new_password: password to set + """ response = self._prepare_request('POST', 'users/change_pw', data={'password': new_password}) return self._check_json_response(response) def user_registrations(self, pythonify: bool = False) -> Union[Dict, List[MISPInbox]]: - """Get all the user registrations.""" + """Get all the user registrations + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'users/registrations/index') registrations = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in registrations: @@ -1423,7 +1762,16 @@ class PyMISP: role: Optional[Union[MISPRole, int, str]] = None, perm_sync: bool = False, perm_publish: bool = False, perm_admin: bool = False, unsafe_fallback: bool = False): - """Accept a user registration""" + """Accept a user registration + + :param registration: the registration to accept + :param organisation: user organization + :param role: user role + :param perm_sync: indicator for sync + :param perm_publish: indicator for publish + :param perm_admin: indicator for admin + :param unsafe_fallback: indicator for unsafe fallback + """ registration_id = get_uuid_or_id_from_abstract_misp(registration) if role: role_id = role_id = get_uuid_or_id_from_abstract_misp(role) @@ -1461,7 +1809,10 @@ class PyMISP: return self._check_json_response(r) def discard_user_registration(self, registration: Union[MISPInbox, int, str, UUID]): - """Discard a user registration""" + """Discard a user registration + + :param registration: the registration to discard + """ registration_id = get_uuid_or_id_from_abstract_misp(registration) r = self._prepare_request('POST', f'users/discardRegistrations/{registration_id}') return self._check_json_response(r) @@ -1471,7 +1822,10 @@ class PyMISP: # ## BEGIN Role ### def roles(self, pythonify: bool = False) -> Union[Dict, List[MISPRole]]: - """Get the existing roles""" + """Get the existing roles + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'roles/index') roles = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in roles: @@ -1484,7 +1838,10 @@ class PyMISP: return to_return def set_default_role(self, role: Union[MISPRole, int, str, UUID]) -> Dict: - """Set a default role for the new user accounts""" + """Set a default role for the new user accounts + + :param role: the default role to set + """ role_id = get_uuid_or_id_from_abstract_misp(role) url = urljoin(self.root_url, f'/admin/roles/set_default/{role_id}') response = self._prepare_request('POST', url) @@ -1579,7 +1936,7 @@ class PyMISP: Deprecated: - :param quickFilter: synponym for quick_filter + :param quickFilter: synonym for quick_filter :param withAttachments: synonym for with_attachments :param last: synonym for publish_timestamp :param enforceWarninglist: synonym for enforce_warninglist @@ -1949,7 +2306,10 @@ class PyMISP: # ## BEGIN Communities ### def communities(self, pythonify: bool = False) -> Union[Dict, List[MISPCommunity]]: - """Get all the communities.""" + """Get all the communities + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'communities/index') communities = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in communities: @@ -1962,7 +2322,11 @@ class PyMISP: return to_return def get_community(self, community: Union[MISPCommunity, int, str, UUID], pythonify: bool = False) -> Union[Dict, MISPCommunity]: - '''Get an community from a MISP instance''' + """Get a community by id from a MISP instance + + :param community: community to get + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ community_id = get_uuid_or_id_from_abstract_misp(community) r = self._prepare_request('GET', f'communities/view/{community_id}') community_j = self._check_json_response(r) @@ -1981,7 +2345,19 @@ class PyMISP: message: Optional[str] = None, sync: bool = False, anonymise_requestor_server: bool = False, mock: bool = False) -> Dict: - """Request the access to a community""" + """Request the access to a community + + :param community: community to request access + :param requestor_email_address: requestor email + :param requestor_gpg_key: requestor key + :param requestor_organisation_name: requestor org name + :param requestor_organisation_uuid: requestor org ID + :param requestor_organisation_description: requestor org desc + :param message: requestor message + :param sync: synchronize flag + :param anonymise_requestor_server: anonymise flag + :param mock: mock flag + """ community_id = get_uuid_or_id_from_abstract_misp(community) to_post = {'org_name': requestor_organisation_name, 'org_uuid': requestor_organisation_uuid, @@ -1997,7 +2373,10 @@ class PyMISP: # ## BEGIN Event Delegation ### def event_delegations(self, pythonify: bool = False) -> Union[Dict, List[MISPEventDelegation]]: - """Get all the event delegations.""" + """Get all the event delegations + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'eventDelegations') delegations = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in delegations: @@ -2010,13 +2389,21 @@ class PyMISP: return to_return def accept_event_delegation(self, delegation: Union[MISPEventDelegation, int, str], pythonify: bool = False) -> Dict: - """Accept the delegation of an event""" + """Accept the delegation of an event + + :param delegation: event delegation to accept + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ delegation_id = get_uuid_or_id_from_abstract_misp(delegation) r = self._prepare_request('POST', f'eventDelegations/acceptDelegation/{delegation_id}') return self._check_json_response(r) def discard_event_delegation(self, delegation: Union[MISPEventDelegation, int, str], pythonify: bool = False) -> Dict: - """Discard the delegation of an event""" + """Discard the delegation of an event + + :param delegation: event delegation to discard + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ delegation_id = get_uuid_or_id_from_abstract_misp(delegation) r = self._prepare_request('POST', f'eventDelegations/deleteDelegation/{delegation_id}') return self._check_json_response(r) @@ -2025,8 +2412,15 @@ class PyMISP: organisation: Optional[Union[MISPOrganisation, int, str, UUID]] = None, event_delegation: Optional[MISPEventDelegation] = None, distribution: int = -1, message: str = '', pythonify: bool = False) -> Union[Dict, MISPEventDelegation]: - '''Delegates an event. - Note: distribution == -1 means recipient decides''' + """Delegate an event. Either event and organisation OR event_delegation are required + + :param event: event to delegate + :param organisation: organization + :param event_delegation: event delegation + :param distribution: distribution == -1 means recipient decides + :param message: message + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if event and organisation: event_id = get_uuid_or_id_from_abstract_misp(event) organisation_id = get_uuid_or_id_from_abstract_misp(organisation) @@ -2048,13 +2442,22 @@ class PyMISP: # ## BEGIN Others ### def push_event_to_ZMQ(self, event: Union[MISPEvent, int, str, UUID]) -> Dict: - """Force push an event on ZMQ""" + """Force push an event by id on ZMQ + + :param event: the event to push + """ event_id = get_uuid_or_id_from_abstract_misp(event) response = self._prepare_request('POST', f'events/pushEventToZMQ/{event_id}.json') return self._check_json_response(response) def direct_call(self, url: str, data: Optional[Dict] = None, params: Mapping = {}, kw_params: Mapping = {}) -> Any: - '''Very lightweight call that posts a data blob (python dictionary or json string) on the URL''' + """Very lightweight call that posts a data blob (python dictionary or json string) on the URL + + :param url: URL to post to + :param data: data to post + :param params: dict with parameters for request + :param kw_params: dict with keyword parameters for request + """ if data is None: response = self._prepare_request('GET', url, params=params, kw_params=kw_params) else: @@ -2063,7 +2466,17 @@ class PyMISP: def freetext(self, event: Union[MISPEvent, int, str, UUID], string: str, adhereToWarninglists: Union[bool, str] = False, distribution: Optional[int] = None, returnMetaAttributes: bool = False, pythonify: bool = False, **kwargs) -> Union[Dict, List[MISPAttribute]]: - """Pass a text to the freetext importer""" + """Pass a text to the freetext importer + + :param event: event + :param string: query + :param adhereToWarninglists: flag + :param distribution: distribution == -1 means recipient decides + :param returnMetaAttributes: flag + :param pythonify: Returns a PyMISP Object instead of the plain json output + :param kwargs: kwargs passed to prepare_request + """ + event_id = get_uuid_or_id_from_abstract_misp(event) query: Dict[str, Any] = {"value": string} wl_params = [False, True, 'soft'] @@ -2088,6 +2501,7 @@ class PyMISP: def upload_stix(self, path, version: str = '2'): """Upload a STIX file to MISP. + :param path: Path to the STIX on the disk (can be a path-like object, or a pseudofile) :param version: Can be 1 or 2 """ @@ -2113,7 +2527,11 @@ class PyMISP: # ## BEGIN Statistics ### def attributes_statistics(self, context: str = 'type', percentage: bool = False) -> Dict: - """Get attributes statistics from the MISP instance.""" + """Get attribute statistics from the MISP instance + + :param context: "type" or "category" + :param percentage: get percentages + """ # FIXME: https://github.com/MISP/MISP/issues/4874 if context not in ['type', 'category']: raise PyMISPError('context can only be "type" or "category"') @@ -2125,7 +2543,11 @@ class PyMISP: return self._check_json_response(response) def tags_statistics(self, percentage: bool = False, name_sort: bool = False) -> Dict: - """Get tags statistics from the MISP instance""" + """Get tag statistics from the MISP instance + + :param percentage: get percentages + :param name_sort: sort by name + """ # FIXME: https://github.com/MISP/MISP/issues/4874 # NOTE: https://github.com/MISP/MISP/issues/4879 if percentage: @@ -2140,7 +2562,10 @@ class PyMISP: return self._check_json_response(response) def users_statistics(self, context: str = 'data') -> Dict: - """Get users statistics from the MISP instance""" + """Get user statistics from the MISP instance + + :param context: one of 'data', 'orgs', 'users', 'tags', 'attributehistogram', 'sightings', 'galaxyMatrix' + """ availables_contexts = ['data', 'orgs', 'users', 'tags', 'attributehistogram', 'sightings', 'galaxyMatrix'] if context not in availables_contexts: raise PyMISPError("context can only be {','.join(availables_contexts)}") @@ -2152,7 +2577,10 @@ class PyMISP: # ## BEGIN User Settings ### def user_settings(self, pythonify: bool = False) -> Union[Dict, List[MISPUserSetting]]: - """Get all the user settings.""" + """Get all the user settings + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'userSettings/index') user_settings = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in user_settings: @@ -2166,7 +2594,12 @@ class PyMISP: def get_user_setting(self, user_setting: str, user: Optional[Union[MISPUser, int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, MISPUserSetting]: - '''Get an user setting''' + """Get a user setting + + :param user_setting: name of user setting + :param user: user + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ query: Dict[str, Any] = {'setting': user_setting} if user: query['user_id'] = get_uuid_or_id_from_abstract_misp(user) @@ -2180,7 +2613,13 @@ class PyMISP: def set_user_setting(self, user_setting: str, value: Union[str, dict], user: Optional[Union[MISPUser, int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, MISPUserSetting]: - '''Get an user setting''' + """Set a user setting + + :param user_setting: name of user setting + :param value: value to set + :param user: user + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ query: Dict[str, Any] = {'setting': user_setting} if isinstance(value, dict): value = json.dumps(value) @@ -2196,7 +2635,11 @@ class PyMISP: return u def delete_user_setting(self, user_setting: str, user: Optional[Union[MISPUser, int, str, UUID]] = None) -> Dict: - '''Delete a user setting''' + """Delete a user setting + + :param user_setting: name of user setting + :param user: user + """ query: Dict[str, Any] = {'setting': user_setting} if user: query['user_id'] = get_uuid_or_id_from_abstract_misp(user) @@ -2208,7 +2651,10 @@ class PyMISP: # ## BEGIN Blocklists ### def event_blocklists(self, pythonify: bool = False) -> Union[Dict, List[MISPEventBlocklist]]: - """Get all the blocklisted events""" + """Get all the blocklisted events + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'eventBlocklists/index') event_blocklists = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in event_blocklists: @@ -2221,7 +2667,10 @@ class PyMISP: return to_return def organisation_blocklists(self, pythonify: bool = False) -> Union[Dict, List[MISPOrganisationBlocklist]]: - """Get all the blocklisted organisations""" + """Get all the blocklisted organisations + + :param pythonify: Returns a list of PyMISP Objects instead of the plain json output. Warning: it might use a lot of RAM + """ r = self._prepare_request('GET', 'orgBlocklists/index') organisation_blocklists = self._check_json_response(r) if not (self.global_pythonify or pythonify) or 'errors' in organisation_blocklists: @@ -2250,12 +2699,23 @@ class PyMISP: def add_event_blocklist(self, uuids: Union[str, List[str]], comment: Optional[str] = None, event_info: Optional[str] = None, event_orgc: Optional[str] = None) -> Dict: - '''Add a new event in the blocklist''' + """Add a new event in the blocklist + + :param uuids: UUIDs + :param comment: comment + :param event_info: event information + :param event_orgc: event organization + """ return self._add_entries_to_blocklist('event', uuids=uuids, comment=comment, event_info=event_info, event_orgc=event_orgc) def add_organisation_blocklist(self, uuids: Union[str, List[str]], comment: Optional[str] = None, org_name: Optional[str] = None) -> Dict: - '''Add a new organisation in the blocklist''' + """Add a new organisation in the blocklist + + :param uuids: UUIDs + :param comment: comment + :param org_name: organization name + """ return self._add_entries_to_blocklist('organisation', uuids=uuids, comment=comment, org_name=org_name) def _update_entries_in_blocklist(self, blocklist_type: str, uuid, **kwargs) -> Dict: @@ -2270,7 +2730,12 @@ class PyMISP: return self._check_json_response(r) def update_event_blocklist(self, event_blocklist: MISPEventBlocklist, event_blocklist_id: Optional[Union[int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, MISPEventBlocklist]: - '''Update an event in the blocklist''' + """Update an event in the blocklist + + :param event_blocklist: event block list + :param event_blocklist_id: event block lisd id + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if event_blocklist_id is None: eblid = get_uuid_or_id_from_abstract_misp(event_blocklist) else: @@ -2283,7 +2748,12 @@ class PyMISP: return e def update_organisation_blocklist(self, organisation_blocklist: MISPOrganisationBlocklist, organisation_blocklist_id: Optional[Union[int, str, UUID]] = None, pythonify: bool = False) -> Union[Dict, MISPOrganisationBlocklist]: - '''Update an organisation in the blocklist''' + """Update an organisation in the blocklist + + :param organisation_blocklist: organization block list + :param organisation_blocklist_id: organization block lisd id + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ if organisation_blocklist_id is None: oblid = get_uuid_or_id_from_abstract_misp(organisation_blocklist) else: @@ -2296,13 +2766,19 @@ class PyMISP: return o def delete_event_blocklist(self, event_blocklist: Union[MISPEventBlocklist, str, UUID]) -> Dict: - '''Delete a blocklisted event''' + """Delete a blocklisted event by id + + :param event_blocklist: event block list to delete + """ event_blocklist_id = get_uuid_or_id_from_abstract_misp(event_blocklist) response = self._prepare_request('POST', f'eventBlocklists/delete/{event_blocklist_id}') return self._check_json_response(response) def delete_organisation_blocklist(self, organisation_blocklist: Union[MISPOrganisationBlocklist, str, UUID]) -> Dict: - '''Delete a blocklisted organisation''' + """Delete a blocklisted organisation by id + + :param organisation_blocklist: organization block list to delete + """ org_blocklist_id = get_uuid_or_id_from_abstract_misp(organisation_blocklist) response = self._prepare_request('POST', f'orgBlocklists/delete/{org_blocklist_id}') return self._check_json_response(response) @@ -2312,7 +2788,12 @@ class PyMISP: # ## BEGIN Global helpers ### def change_sharing_group_on_entity(self, misp_entity: Union[MISPEvent, MISPAttribute, MISPObject], sharing_group_id, pythonify: bool = False) -> Union[Dict, MISPEvent, MISPObject, MISPAttribute, MISPShadowAttribute]: - """Change the sharing group of an event, an attribute, or an object""" + """Change the sharing group of an event, an attribute, or an object + + :param misp_entity: entity to change + :param sharing_group_id: group to change + :param pythonify: Returns a PyMISP Object instead of the plain json output + """ misp_entity.distribution = 4 # Needs to be 'Sharing group' if 'SharingGroup' in misp_entity: # Delete former SharingGroup information del misp_entity.SharingGroup @@ -2329,7 +2810,12 @@ class PyMISP: raise PyMISPError('The misp_entity must be MISPEvent, MISPObject or MISPAttribute') def tag(self, misp_entity: Union[AbstractMISP, str, dict], tag: Union[MISPTag, str], local: bool = False) -> Dict: - """Tag an event or an attribute. misp_entity can be a MISPEvent, a MISP Attribute, or a UUID""" + """Tag an event or an attribute. + + :param misp_entity: a MISPEvent, a MISP Attribute, or a UUID + :param tag: tag to add + :param local: whether to tag locally + """ if isinstance(misp_entity, AbstractMISP) and 'uuid' in misp_entity: uuid = misp_entity.uuid elif isinstance(misp_entity, dict) and 'uuid' in misp_entity: @@ -2343,7 +2829,11 @@ class PyMISP: return self._check_json_response(response) def untag(self, misp_entity: Union[AbstractMISP, str, dict], tag: Union[MISPTag, str]) -> Dict: - """Untag an event or an attribute. misp_entity can be a UUID""" + """Untag an event or an attribute + + :param misp_entity: misp_entity can be a UUID + :param tag: tag to remove + """ if isinstance(misp_entity, AbstractMISP) and 'uuid' in misp_entity: uuid = misp_entity.uuid elif isinstance(misp_entity, dict) and 'uuid' in misp_entity: @@ -2377,7 +2867,7 @@ class PyMISP: # ## MISP internal tasks ### def get_all_functions(self, not_implemented: bool = False): - '''Get all methods available vi the API allow to get the ones that are not implemented.''' + '''Get all methods available via the API, including ones that are not implemented.''' response = self._prepare_request('GET', '/servers/queryACL/printAllFunctionNames') functions = self._check_json_response(response) # Format as URLs diff --git a/pymisp/mispevent.py b/pymisp/mispevent.py index 2de7861..c424165 100644 --- a/pymisp/mispevent.py +++ b/pymisp/mispevent.py @@ -1030,8 +1030,7 @@ class MISPEvent(AbstractMISP): def to_feed(self, valid_distributions: List[int] = [0, 1, 2, 3, 4, 5], with_meta: bool = False) -> Dict: """ Generate a json output for MISP Feed. - Notes: - * valid_distributions only makes sense if the distribution key is set (i.e. the event is exported from a MISP instance) + Note: valid_distributions only makes sense if the distribution key is set; i.e., the event is exported from a MISP instance. """ required = ['info', 'Orgc'] for r in required: