diff --git a/pymisp/__init__.py b/pymisp/__init__.py index 64c7b1b..a0e8c5f 100644 --- a/pymisp/__init__.py +++ b/pymisp/__init__.py @@ -30,7 +30,7 @@ try: from .exceptions import PyMISPError, NewEventError, NewAttributeError, MissingDependency, NoURL, NoKey, InvalidMISPObject, UnknownMISPObjectTemplate # noqa from .api import PyMISP # noqa from .abstract import AbstractMISP, MISPEncode # noqa - from .mispevent import MISPEvent, MISPAttribute, MISPObjectReference, MISPObjectAttribute, MISPObject, MISPTag, MISPUser, MISPOrganisation # noqa + from .mispevent import MISPEvent, MISPAttribute, MISPObjectReference, MISPObjectAttribute, MISPObject, MISPTag, MISPUser, MISPOrganisation, MISPSighting # noqa from .tools import AbstractMISPObjectGenerator # noqa from .tools import Neo4j # noqa from .tools import stix # noqa diff --git a/pymisp/api.py b/pymisp/api.py index b885b16..75e84b0 100644 --- a/pymisp/api.py +++ b/pymisp/api.py @@ -16,7 +16,7 @@ import zipfile from . import __version__, deprecated from .exceptions import PyMISPError, SearchError, NoURL, NoKey -from .mispevent import MISPEvent, MISPAttribute, MISPUser, MISPOrganisation +from .mispevent import MISPEvent, MISPAttribute, MISPUser, MISPOrganisation, MISPSighting from .abstract import MISPEncode logger = logging.getLogger('pymisp') @@ -1296,10 +1296,16 @@ class PyMISP(object): def set_sightings(self, sightings): """Push a sighting (python dictionary)""" - if isinstance(sightings, dict): - sightings = json.dumps(sightings) + to_post = [] + if not isinstance(sightings, list): + sightings = [sightings] + for sighting in sightings: + if isinstance(sighting, MISPSighting): + to_post.appent(sighting.to_json()) + elif isinstance(sighting, dict): + to_post.append(json.dumps(sightings)) url = urljoin(self.root_url, 'sightings/add/') - response = self.__prepare_request('POST', url, sightings) + response = self.__prepare_request('POST', url, json.dumps(to_post)) return self._check_response(response) def sighting_per_json(self, json_file): @@ -1308,6 +1314,18 @@ class PyMISP(object): jdata = json.load(f) return self.set_sightings(jdata) + def sighting(self, value, source=None, type=None, timestamp=None, **kwargs): + """ Set a single sighting. + :value: Value can either be the attribute's value (to update sighting on all the attributes with this value), + or an UUID in order to update the sightings of one particular attribute. + :source: Source of the sighting + :type: Type of the sighting + :timestamp: Timestamp associated to the sighting + """ + s = MISPSighting() + s.from_dict(value=value, source=source, type=type, timestamp=timestamp, **kwargs) + return self.set_sightings(s) + # ############## Sharing Groups ################## def get_sharing_groups(self): diff --git a/pymisp/mispevent.py b/pymisp/mispevent.py index 55fc57c..abec80f 100644 --- a/pymisp/mispevent.py +++ b/pymisp/mispevent.py @@ -689,6 +689,31 @@ class MISPOrganisation(AbstractMISP): super(MISPOrganisation, self).__init__() +class MISPSighting(AbstractMISP): + + def __init__(self): + super(MISPSighting, self).__init__() + + def from_dict(self, value, source=None, type=None, timestamp=None, **kwargs): + """Initialize the MISPSighting from a dictionary + :value: Value can either be the attribute's value (to update sighting on all the attributes with this value), + or an UUID in order to update the sightings of one particular attribute. + :source: Source of the sighting + :type: Type of the sighting + :timestamp: Timestamp associated to the sighting + """ + self.value = value + self.source = source + self.type = type + self.timestamp = timestamp + super(MISPSighting, self).from_dict(**kwargs) + + def __repr__(self): + if hasattr(self, 'value'): + return '<{self.__class__.__name__}(value={self.value})'.format(self=self) + return '<{self.__class__.__name__}(NotInitialized)'.format(self=self) + + class MISPObjectAttribute(MISPAttribute): def __init__(self, definition):