2019-12-18 14:45:14 +01:00
#!/usr/bin/env python3
2014-03-19 19:10:36 +01:00
# -*- coding: utf-8 -*-
2020-05-07 12:17:31 +02:00
from typing import TypeVar , Optional , Tuple , List , Dict , Union , Any , Mapping , Iterator
2019-12-18 14:45:14 +01:00
from datetime import date , datetime
import csv
from pathlib import Path
import logging
from urllib . parse import urljoin
2015-02-16 14:31:29 +01:00
import json
2019-12-18 14:45:14 +01:00
import requests
from requests . auth import AuthBase
2015-09-18 12:03:56 +02:00
import re
2019-12-18 14:45:14 +01:00
from uuid import UUID
import warnings
import sys
2020-01-23 10:27:40 +01:00
import traceback
2020-05-07 12:17:31 +02:00
import copy
2019-12-18 14:45:14 +01:00
from . import __version__ , everything_broken
2020-05-07 12:17:31 +02:00
from . exceptions import MISPServerError , PyMISPUnexpectedResponse , PyMISPError , NoURL , NoKey
2019-12-18 14:45:14 +01:00
from . mispevent import MISPEvent , MISPAttribute , MISPSighting , MISPLog , MISPObject , \
MISPUser , MISPOrganisation , MISPShadowAttribute , MISPWarninglist , MISPTaxonomy , \
MISPGalaxy , MISPNoticelist , MISPObjectReference , MISPObjectTemplate , MISPSharingGroup , \
2020-05-07 12:17:31 +02:00
MISPRole , MISPServer , MISPFeed , MISPEventDelegation , MISPCommunity , MISPUserSetting , MISPInbox
2019-12-18 14:45:14 +01:00
from . abstract import pymisp_json_default , MISPTag , AbstractMISP , describe_types
SearchType = TypeVar ( ' SearchType ' , str , int )
# str: string to search / list: values to search (OR) / dict: {'OR': [list], 'NOT': [list], 'AND': [list]}
2020-01-23 10:27:40 +01:00
SearchParameterTypes = TypeVar ( ' SearchParameterTypes ' , str , List [ Union [ str , int ] ] , Dict [ str , Union [ str , int ] ] )
2017-11-09 02:33:55 +01:00
2019-12-18 14:45:14 +01:00
ToIDSType = TypeVar ( ' ToIDSType ' , str , int , bool )
2017-03-09 16:32:51 +01:00
2017-11-08 03:10:04 +01:00
logger = logging . getLogger ( ' pymisp ' )
2015-09-18 12:03:56 +02:00
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def get_uuid_or_id_from_abstract_misp ( obj : Union [ AbstractMISP , int , str , UUID ] ) - > Union [ str , int ] :
if isinstance ( obj , UUID ) :
return str ( obj )
if isinstance ( obj , ( int , str ) ) :
return obj
if isinstance ( obj , dict ) and len ( obj . keys ( ) ) == 1 :
# We have an object in that format: {'Event': {'id': 2, ...}}
# We need to get the content of that dictionary
obj = obj [ list ( obj . keys ( ) ) [ 0 ] ]
if isinstance ( obj , MISPShadowAttribute ) :
# A ShadowAttribute has the same UUID as the related Attribute, we *need* to use the ID
return obj [ ' id ' ]
if isinstance ( obj , MISPEventDelegation ) :
# An EventDelegation doesn't have a uuid, we *need* to use the ID
return obj [ ' id ' ]
if ' uuid ' in obj :
return obj [ ' uuid ' ]
return obj [ ' id ' ]
def register_user ( misp_url : str , email : str ,
organisation : Union [ MISPOrganisation , int , str , UUID ] = None ,
org_id : Optional [ str ] = None , org_name : Optional [ str ] = None ,
message : Optional [ str ] = None , custom_perms : Optional [ str ] = None ,
perm_sync : bool = False , perm_publish : bool = False , perm_admin : bool = False ,
verify : bool = True ) - > Dict :
""" Ask for the creation of an account for the user with the given email address """
data = copy . deepcopy ( locals ( ) )
if organisation :
data [ ' org_uuid ' ] = get_uuid_or_id_from_abstract_misp ( data . pop ( ' organisation ' ) )
url = urljoin ( data . pop ( ' misp_url ' ) , ' /users/register ' )
user_agent = f ' PyMISP { __version__ } - no login - Python { " . " . join ( str ( x ) for x in sys . version_info [ : 2 ] ) } '
headers = {
' Accept ' : ' application/json ' ,
' content-type ' : ' application/json ' ,
' User-Agent ' : user_agent }
r = requests . post ( url , json = data , verify = data . pop ( ' verify ' ) , headers = headers )
return r . json ( )
2019-12-18 14:45:14 +01:00
class PyMISP :
2017-01-16 15:27:44 +01:00
""" Python API for MISP
: param url : URL of the MISP instance you want to connect to
: param key : API key of the user you want to use
2020-05-14 09:42:24 +02:00
: 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 )
2017-11-10 23:56:53 +01:00
: param debug : Write all the debug information to stderr
2017-01-16 15:27:44 +01:00
: param proxies : Proxy dict as describes here : http : / / docs . python - requests . org / en / master / user / advanced / #proxies
2018-02-26 17:04:38 +01:00
: param cert : Client certificate , as described there : http : / / docs . python - requests . org / en / master / user / advanced / #client-side-certificates
2019-02-27 20:13:43 +01:00
: param auth : The auth parameter is passed directly to requests , as described here : http : / / docs . python - requests . org / en / master / user / authentication /
2019-04-30 11:38:53 +02:00
: param tool : The software using PyMISP ( string ) , used to set a unique user - agent
2020-05-21 20:31:19 +02:00
: param timeout : Timeout as described here : https : / / requests . readthedocs . io / en / master / user / advanced / #timeouts
2014-04-16 14:09:56 +02:00
"""
2020-05-07 12:17:31 +02:00
def __init__ ( self , url : str , key : str , ssl : bool = True , debug : bool = False , proxies : Mapping = { } ,
2020-05-21 23:03:04 +02:00
cert : Tuple [ str , tuple ] = None , auth : AuthBase = None , tool : str = ' ' , timeout : Optional [ Union [ float , Tuple [ float , float ] ] ] = None ) :
2015-09-18 17:48:10 +02:00
if not url :
raise NoURL ( ' Please provide the URL of your MISP instance. ' )
if not key :
raise NoKey ( ' Please provide your authorization key. ' )
2020-05-07 12:17:31 +02:00
self . root_url : str = url
self . key : str = key
self . ssl : bool = ssl
self . proxies : Mapping [ str , str ] = proxies
self . cert : Optional [ Tuple [ str , tuple ] ] = cert
self . auth : Optional [ AuthBase ] = auth
self . tool : str = tool
2020-05-21 23:03:04 +02:00
self . timeout : Optional [ Union [ float , Tuple [ float , float ] ] ] = timeout
2017-02-16 16:23:42 +01:00
2019-12-18 14:45:14 +01:00
self . global_pythonify = False
self . resources_path = Path ( __file__ ) . parent / ' data '
2017-11-08 04:10:54 +01:00
if debug :
logger . setLevel ( logging . DEBUG )
logger . info ( ' To configure logging in your script, leave it to None and use the following: import logging; logging.getLogger( \' pymisp \' ).setLevel(logging.DEBUG) ' )
2014-04-11 18:45:52 +02:00
2015-09-21 14:40:06 +02:00
try :
# Make sure the MISP instance is working and the URL is valid
2019-12-18 14:45:14 +01:00
response = self . recommended_pymisp_version
2020-01-23 10:27:40 +01:00
if ' errors ' in response :
logger . warning ( response [ ' errors ' ] [ 0 ] )
2017-01-27 11:58:00 +01:00
else :
2017-05-03 14:47:06 +02:00
pymisp_version_tup = tuple ( int ( x ) for x in __version__ . split ( ' . ' ) )
recommended_version_tup = tuple ( int ( x ) for x in response [ ' version ' ] . split ( ' . ' ) )
2017-10-28 22:57:03 +02:00
if recommended_version_tup < pymisp_version_tup [ : 3 ] :
2019-12-18 14:45:14 +01:00
logger . info ( f " The version of PyMISP recommended by the MISP instance (response[ ' version ' ]) is older than the one you ' re using now ( { __version__ } ). If you have a problem, please upgrade the MISP instance or use an older PyMISP version. " )
2017-10-28 22:57:03 +02:00
elif pymisp_version_tup [ : 3 ] < recommended_version_tup :
2019-12-18 14:45:14 +01:00
logger . warning ( f " The version of PyMISP recommended by the MISP instance ( { response [ ' version ' ] } ) is newer than the one you ' re using now ( { __version__ } ). Please upgrade PyMISP. " )
misp_version = self . misp_instance_version
if ' version ' in misp_version :
self . _misp_version = tuple ( int ( v ) for v in misp_version [ ' version ' ] . split ( ' . ' ) )
2016-12-14 15:42:43 +01:00
2019-12-18 14:45:14 +01:00
# Get the user information
2020-01-23 10:27:40 +01:00
self . _current_user : MISPUser
self . _current_role : MISPRole
self . _current_user_settings : List [ MISPUserSetting ]
2019-12-18 14:45:14 +01:00
self . _current_user , self . _current_role , self . _current_user_settings = self . get_user ( pythonify = True , expanded = True )
2015-09-21 14:40:06 +02:00
except Exception as e :
2020-01-23 10:27:40 +01:00
traceback . print_exc ( )
2019-12-18 14:45:14 +01:00
raise PyMISPError ( f ' Unable to connect to MISP ( { self . root_url } ). Please make sure the API key and the URL are correct (http/https is required): { e } ' )
2015-09-21 14:40:06 +02:00
2016-10-10 12:24:17 +02:00
try :
2019-12-18 14:45:14 +01:00
self . describe_types = self . describe_types_remote
2017-05-03 13:22:15 +02:00
except Exception :
2019-12-18 14:45:14 +01:00
self . describe_types = self . describe_types_local
2016-10-10 12:24:17 +02:00
self . categories = self . describe_types [ ' categories ' ]
self . types = self . describe_types [ ' types ' ]
self . category_type_mapping = self . describe_types [ ' category_type_mappings ' ]
self . sane_default = self . describe_types [ ' sane_defaults ' ]
2016-04-14 10:29:36 +02:00
2020-05-07 12:17:31 +02:00
def remote_acl ( self , debug_type : str = ' findMissingFunctionNames ' ) - > Dict :
2019-12-18 14:45:14 +01:00
""" This should return an empty list, unless the ACL is outdated.
debug_type can only be printAllFunctionNames , findMissingFunctionNames , or printRoleAccess
"""
response = self . _prepare_request ( ' GET ' , f ' events/queryACL/ { debug_type } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-12-01 14:35:19 +01:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def describe_types_local ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Returns the content of describe types from the package '''
2019-10-08 08:15:56 +02:00
return describe_types
2017-12-01 16:15:46 +01:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def describe_types_remote ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Returns the content of describe types from the remote instance '''
response = self . _prepare_request ( ' GET ' , ' attributes/describeTypes.json ' )
2020-01-23 10:27:40 +01:00
remote_describe_types = self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
return remote_describe_types [ ' result ' ]
2014-04-11 18:45:52 +02:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def recommended_pymisp_version ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Returns the recommended API version from the server """
response = self . _prepare_request ( ' GET ' , ' servers/getPyMISPVersion.json ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-03-09 16:32:51 +01:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def version ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Returns the version of PyMISP you ' re curently using """
return { ' version ' : __version__ }
2018-06-07 11:13:51 +02:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def pymisp_version_master ( self ) - > Dict :
2020-06-16 14:58:38 +02:00
return self . pymisp_version_main
@property
def pymisp_version_main ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Get the most recent version of PyMISP from github """
2020-06-16 14:58:38 +02:00
r = requests . get ( ' https://raw.githubusercontent.com/MISP/PyMISP/main/pymisp/__init__.py ' )
2019-12-18 14:45:14 +01:00
if r . status_code == 200 :
version = re . findall ( " __version__ = ' (.*) ' " , r . text )
return { ' version ' : version [ 0 ] }
2020-06-16 14:58:38 +02:00
return { ' error ' : ' Impossible to retrieve the version of the main branch. ' }
2015-12-19 17:57:29 +01:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def misp_instance_version ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Returns the version of the instance. """
response = self . _prepare_request ( ' GET ' , ' servers/getVersion.json ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2018-06-07 11:13:51 +02:00
2019-12-18 14:45:14 +01:00
@property
2020-05-07 12:17:31 +02:00
def misp_instance_version_master ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Get the most recent version from github """
r = requests . get ( ' https://raw.githubusercontent.com/MISP/MISP/2.4/VERSION.json ' )
if r . status_code == 200 :
master_version = json . loads ( r . text )
return { ' version ' : ' {} . {} . {} ' . format ( master_version [ ' major ' ] , master_version [ ' minor ' ] , master_version [ ' hotfix ' ] ) }
return { ' error ' : ' Impossible to retrieve the version of the master branch. ' }
2015-12-19 17:57:29 +01:00
2020-05-07 12:17:31 +02:00
def update_misp ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , ' /servers/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2015-09-23 18:47:47 +02:00
2020-05-07 12:17:31 +02:00
def set_server_setting ( self , setting : str , value : Union [ str , int , bool ] , force : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
data = { ' value ' : value , ' force ' : force }
response = self . _prepare_request ( ' POST ' , f ' /servers/serverSettingsEdit/ { setting } ' , data = data )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def get_server_setting ( self , setting : str ) - > Dict :
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' GET ' , f ' /servers/getSetting/ { setting } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def server_settings ( self ) - > Dict :
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' GET ' , ' /servers/serverSettings ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-07-20 15:33:15 +02:00
2020-05-07 12:17:31 +02:00
def restart_workers ( self ) - > Dict :
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' POST ' , ' /servers/restartWorkers ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2014-04-11 18:45:52 +02:00
2020-05-07 12:17:31 +02:00
def db_schema_diagnostic ( self ) - > Dict :
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' GET ' , ' /servers/dbSchemaDiagnostic ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2014-04-11 18:45:52 +02:00
2020-01-03 15:42:15 +01:00
def toggle_global_pythonify ( self ) - > None :
2019-12-18 14:45:14 +01:00
self . global_pythonify = not self . global_pythonify
2014-04-16 14:09:56 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Event ##
2014-04-11 18:45:52 +02:00
2020-05-07 12:17:31 +02:00
def events ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPEvent ] ] :
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , ' events ' )
2020-01-23 10:27:40 +01:00
events_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in events_r :
return events_r
2019-12-18 14:45:14 +01:00
to_return = [ ]
2020-01-03 15:42:15 +01:00
for event in events_r :
2019-12-18 14:45:14 +01:00
e = MISPEvent ( )
e . from_dict ( * * event )
to_return . append ( e )
return to_return
2018-12-06 14:26:23 +01:00
2020-04-28 11:17:24 +02:00
def get_event ( self , event : Union [ MISPEvent , int , str , UUID ] ,
deleted : Union [ bool , int , list ] = False ,
extended : Union [ bool , int ] = False ,
2020-05-07 12:17:31 +02:00
pythonify : bool = False ) - > Union [ Dict , MISPEvent ] :
2019-12-18 14:45:14 +01:00
''' Get an event from a MISP instance '''
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-04-28 11:17:24 +02:00
data = { }
2019-12-18 14:45:14 +01:00
if deleted :
2020-04-28 11:17:24 +02:00
data [ ' deleted ' ] = deleted
if extended :
2020-05-04 02:58:33 +02:00
data [ ' extended ' ] = extended
2020-04-28 11:17:24 +02:00
if data :
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' events/view/ { event_id } ' , data = data )
2019-12-18 14:45:14 +01:00
else :
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' events/view/ { event_id } ' )
2020-01-23 10:27:40 +01:00
event_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in event_r :
return event_r
2019-12-18 14:45:14 +01:00
e = MISPEvent ( )
2020-01-03 15:42:15 +01:00
e . load ( event_r )
2019-12-18 14:45:14 +01:00
return e
2018-12-06 14:26:23 +01:00
2020-05-07 12:17:31 +02:00
def add_event ( self , event : MISPEvent , pythonify : bool = False ) - > Union [ Dict , MISPEvent ] :
2019-12-18 14:45:14 +01:00
''' Add a new event on a MISP instance '''
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , ' events ' , data = event )
2020-01-23 10:27:40 +01:00
new_event = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_event :
return new_event
e = MISPEvent ( )
e . load ( new_event )
return e
2018-12-06 14:26:23 +01:00
2020-05-07 12:17:31 +02:00
def update_event ( self , event : MISPEvent , event_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPEvent ] :
2019-12-18 14:45:14 +01:00
''' Update an event on a MISP instance '''
if event_id is None :
2020-05-07 12:17:31 +02:00
eid = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
else :
2020-05-07 12:17:31 +02:00
eid = get_uuid_or_id_from_abstract_misp ( event_id )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' events/ { eid } ' , data = event )
2020-01-23 10:27:40 +01:00
updated_event = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_event :
return updated_event
e = MISPEvent ( )
e . load ( updated_event )
return e
2018-12-06 14:26:23 +01:00
2020-05-07 12:17:31 +02:00
def delete_event ( self , event : Union [ MISPEvent , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete an event from a MISP instance '''
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' DELETE ' , f ' events/delete/ { event_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-01-16 20:41:32 +01:00
2020-05-07 12:17:31 +02:00
def publish ( self , event : Union [ MISPEvent , int , str , UUID ] , alert : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
""" 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 .
2014-04-11 18:45:52 +02:00
"""
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
if alert :
response = self . _prepare_request ( ' POST ' , f ' events/alert/ { event_id } ' )
else :
response = self . _prepare_request ( ' POST ' , f ' events/publish/ { event_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def contact_event_reporter ( self , event : Union [ MISPEvent , int , str , UUID ] , message : str ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Send a message to the reporter of an event """
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
to_post = { ' message ' : message }
response = self . _prepare_request ( ' POST ' , f ' events/contact/ { event_id } ' , data = to_post )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Event ###
# ## BEGIN Object ###
2020-05-07 12:17:31 +02:00
def get_object ( self , misp_object : Union [ MISPObject , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPObject ] :
2019-12-18 14:45:14 +01:00
''' Get an object from the remote MISP instance '''
2020-05-07 12:17:31 +02:00
object_id = get_uuid_or_id_from_abstract_misp ( misp_object )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' objects/view/ { object_id } ' )
2020-01-23 10:27:40 +01:00
misp_object_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in misp_object_r :
return misp_object_r
o = MISPObject ( misp_object_r [ ' Object ' ] [ ' name ' ] )
o . from_dict ( * * misp_object_r )
2019-12-18 14:45:14 +01:00
return o
2020-05-07 12:17:31 +02:00
def add_object ( self , event : Union [ MISPEvent , int , str , UUID ] , misp_object : MISPObject , pythonify : bool = False ) - > Union [ Dict , MISPObject ] :
2019-12-18 14:45:14 +01:00
''' Add a MISP Object to an existing MISP event '''
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' objects/add/ { event_id } ' , data = misp_object )
2020-01-23 10:27:40 +01:00
new_object = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_object :
return new_object
o = MISPObject ( new_object [ ' Object ' ] [ ' name ' ] )
o . from_dict ( * * new_object )
return o
2020-05-07 12:17:31 +02:00
def update_object ( self , misp_object : MISPObject , object_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPObject ] :
2019-12-18 14:45:14 +01:00
''' Update an object on a MISP instance '''
if object_id is None :
2020-05-07 12:17:31 +02:00
oid = get_uuid_or_id_from_abstract_misp ( misp_object )
2019-12-18 14:45:14 +01:00
else :
2020-05-07 12:17:31 +02:00
oid = get_uuid_or_id_from_abstract_misp ( object_id )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' objects/edit/ { oid } ' , data = misp_object )
2020-01-23 10:27:40 +01:00
updated_object = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_object :
return updated_object
o = MISPObject ( updated_object [ ' Object ' ] [ ' name ' ] )
o . from_dict ( * * updated_object )
return o
2020-05-07 12:17:31 +02:00
def delete_object ( self , misp_object : Union [ MISPObject , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete an object from a MISP instance '''
2020-05-07 12:17:31 +02:00
object_id = get_uuid_or_id_from_abstract_misp ( misp_object )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' objects/delete/ { object_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def add_object_reference ( self , misp_object_reference : MISPObjectReference , pythonify : bool = False ) - > Union [ Dict , MISPObjectReference ] :
2019-12-18 14:45:14 +01:00
""" Add a reference to an object """
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , ' object_references/add ' , misp_object_reference )
2020-01-23 10:27:40 +01:00
object_reference = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in object_reference :
return object_reference
2020-01-03 15:42:15 +01:00
ref = MISPObjectReference ( )
ref . from_dict ( * * object_reference )
return ref
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def delete_object_reference ( self , object_reference : Union [ MISPObjectReference , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Delete a reference to an object """
2020-05-07 12:17:31 +02:00
object_reference_id = get_uuid_or_id_from_abstract_misp ( object_reference )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' object_references/delete/ { object_reference_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# Object templates
2020-05-07 12:17:31 +02:00
def object_templates ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPObjectTemplate ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the object templates. """
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , ' objectTemplates ' )
2020-01-23 10:27:40 +01:00
templates = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in templates :
return templates
2019-12-18 14:45:14 +01:00
to_return = [ ]
2020-01-03 15:42:15 +01:00
for object_template in templates :
2019-12-18 14:45:14 +01:00
o = MISPObjectTemplate ( )
o . from_dict ( * * object_template )
to_return . append ( o )
return to_return
2018-04-21 16:29:38 +02:00
2020-05-07 12:17:31 +02:00
def get_object_template ( self , object_template : Union [ MISPObjectTemplate , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPObjectTemplate ] :
2019-12-18 14:45:14 +01:00
""" Gets the full object template corresponting the UUID passed as parameter """
2020-05-07 12:17:31 +02:00
object_template_id = get_uuid_or_id_from_abstract_misp ( object_template )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' objectTemplates/view/ { object_template_id } ' )
2020-01-23 10:27:40 +01:00
object_template_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in object_template_r :
return object_template_r
2019-12-18 14:45:14 +01:00
t = MISPObjectTemplate ( )
2020-01-03 15:42:15 +01:00
t . from_dict ( * * object_template_r )
2019-12-18 14:45:14 +01:00
return t
2018-04-21 16:29:38 +02:00
2020-05-07 12:17:31 +02:00
def update_object_templates ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Trigger an update of the object templates """
response = self . _prepare_request ( ' POST ' , ' objectTemplates/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2014-04-11 18:45:52 +02:00
2019-12-18 14:45:14 +01:00
# ## END Object ###
2014-04-16 14:09:56 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Attribute ###
2014-04-11 18:45:52 +02:00
2020-05-07 12:17:31 +02:00
def attributes ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPAttribute ] ] :
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' GET ' , ' attributes/index ' )
2020-01-23 10:27:40 +01:00
attributes_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in attributes_r :
return attributes_r
2019-12-18 14:45:14 +01:00
to_return = [ ]
2020-01-03 15:42:15 +01:00
for attribute in attributes_r :
2019-12-18 14:45:14 +01:00
a = MISPAttribute ( )
a . from_dict ( * * attribute )
to_return . append ( a )
return to_return
2014-04-16 14:09:56 +02:00
2020-05-07 12:17:31 +02:00
def get_attribute ( self , attribute : Union [ MISPAttribute , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPAttribute ] :
2019-12-18 14:45:14 +01:00
''' Get an attribute from a MISP instance '''
2020-05-07 12:17:31 +02:00
attribute_id = get_uuid_or_id_from_abstract_misp ( attribute )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' attributes/view/ { attribute_id } ' )
2020-01-23 10:27:40 +01:00
attribute_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in attribute_r :
return attribute_r
2019-12-18 14:45:14 +01:00
a = MISPAttribute ( )
2020-01-03 15:42:15 +01:00
a . from_dict ( * * attribute_r )
2019-12-18 14:45:14 +01:00
return a
2020-05-07 12:17:31 +02:00
def add_attribute ( self , event : Union [ MISPEvent , int , str , UUID ] , attribute : MISPAttribute , pythonify : bool = False ) - > Union [ Dict , MISPAttribute , MISPShadowAttribute ] :
2019-12-18 14:45:14 +01:00
''' 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 } } '''
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' attributes/add/ { event_id } ' , data = attribute )
2020-01-23 10:27:40 +01:00
new_attribute = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if isinstance ( attribute , list ) :
# Multiple attributes were passed at once, the handling is totally different
if not ( self . global_pythonify or pythonify ) :
return new_attribute
to_return = { ' attributes ' : [ ] }
if ' errors ' in new_attribute :
to_return [ ' errors ' ] = new_attribute [ ' errors ' ]
for new_attr in new_attribute [ ' Attribute ' ] :
a = MISPAttribute ( )
a . from_dict ( * * new_attr )
to_return [ ' attributes ' ] . append ( a )
return to_return
2014-04-11 18:45:52 +02:00
2019-12-18 14:45:14 +01:00
if ( ' errors ' in new_attribute and new_attribute [ ' errors ' ] [ 0 ] == 403
and new_attribute [ ' errors ' ] [ 1 ] [ ' message ' ] == ' You do not have permission to do that. ' ) :
# At this point, we assume the user tried to add an attribute on an event they don't own
# Re-try with a proposal
return self . add_attribute_proposal ( event_id , attribute , pythonify )
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_attribute :
return new_attribute
a = MISPAttribute ( )
a . from_dict ( * * new_attribute )
return a
2020-05-07 12:17:31 +02:00
def update_attribute ( self , attribute : MISPAttribute , attribute_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPAttribute , MISPShadowAttribute ] :
2019-12-18 14:45:14 +01:00
''' Update an attribute on a MISP instance '''
if attribute_id is None :
2020-05-07 12:17:31 +02:00
aid = get_uuid_or_id_from_abstract_misp ( attribute )
2017-10-22 20:02:47 +02:00
else :
2020-05-07 12:17:31 +02:00
aid = get_uuid_or_id_from_abstract_misp ( attribute_id )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' attributes/edit/ { aid } ' , data = attribute )
2020-01-23 10:27:40 +01:00
updated_attribute = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if ' errors ' in updated_attribute :
if ( updated_attribute [ ' errors ' ] [ 0 ] == 403
and updated_attribute [ ' errors ' ] [ 1 ] [ ' message ' ] == ' You do not have permission to do that. ' ) :
# At this point, we assume the user tried to update an attribute on an event they don't own
# Re-try with a proposal
2020-01-03 15:42:15 +01:00
return self . update_attribute_proposal ( aid , attribute , pythonify )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_attribute :
return updated_attribute
a = MISPAttribute ( )
a . from_dict ( * * updated_attribute )
return a
2020-05-07 12:17:31 +02:00
def delete_attribute ( self , attribute : Union [ MISPAttribute , int , str , UUID ] , hard : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete an attribute from a MISP instance '''
2020-05-07 12:17:31 +02:00
attribute_id = get_uuid_or_id_from_abstract_misp ( attribute )
2019-12-18 14:45:14 +01:00
data = { }
if hard :
data [ ' hard ' ] = 1
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' attributes/delete/ { attribute_id } ' , data = data )
2020-01-23 10:27:40 +01:00
response = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if ( ' errors ' in response and response [ ' errors ' ] [ 0 ] == 403
and response [ ' errors ' ] [ 1 ] [ ' message ' ] == ' You do not have permission to do that. ' ) :
# FIXME: https://github.com/MISP/MISP/issues/4913
# At this point, we assume the user tried to delete an attribute on an event they don't own
# Re-try with a proposal
return self . delete_attribute_proposal ( attribute_id )
return response
2015-08-28 17:03:35 +02:00
2019-12-18 14:45:14 +01:00
# ## END Attribute ###
2017-08-03 17:17:37 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Attribute Proposal ###
2018-10-04 09:19:48 +02:00
2020-05-07 12:17:31 +02:00
def attribute_proposals ( self , event : Optional [ Union [ MISPEvent , int , str , UUID ] ] = None , pythonify : bool = False ) - > Union [ Dict , List [ MISPShadowAttribute ] ] :
2019-12-18 14:45:14 +01:00
if event :
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' shadow_attributes/index/ { event_id } ' )
2017-01-16 20:41:32 +01:00
else :
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' GET ' , ' shadow_attributes ' )
2020-01-23 10:27:40 +01:00
attribute_proposals = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in attribute_proposals :
return attribute_proposals
to_return = [ ]
for attribute_proposal in attribute_proposals :
a = MISPShadowAttribute ( )
a . from_dict ( * * attribute_proposal )
to_return . append ( a )
return to_return
2017-08-04 19:30:40 +02:00
2020-05-07 12:17:31 +02:00
def get_attribute_proposal ( self , proposal : Union [ MISPShadowAttribute , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPShadowAttribute ] :
proposal_id = get_uuid_or_id_from_abstract_misp ( proposal )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' shadow_attributes/view/ { proposal_id } ' )
2020-01-23 10:27:40 +01:00
attribute_proposal = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in attribute_proposal :
return attribute_proposal
a = MISPShadowAttribute ( )
a . from_dict ( * * attribute_proposal )
return a
# NOTE: the tree following method have a very specific meaning, look at the comments
2020-05-07 12:17:31 +02:00
def add_attribute_proposal ( self , event : Union [ MISPEvent , int , str , UUID ] , attribute : MISPAttribute , pythonify : bool = False ) - > Union [ Dict , MISPShadowAttribute ] :
2019-12-18 14:45:14 +01:00
''' Propose a new attribute in an event '''
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' shadow_attributes/add/ { event_id } ' , data = attribute )
2020-01-23 10:27:40 +01:00
new_attribute_proposal = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_attribute_proposal :
return new_attribute_proposal
a = MISPShadowAttribute ( )
a . from_dict ( * * new_attribute_proposal )
return a
2020-05-07 12:17:31 +02:00
def update_attribute_proposal ( self , initial_attribute : Union [ MISPAttribute , int , str , UUID ] , attribute : MISPAttribute , pythonify : bool = False ) - > Union [ Dict , MISPShadowAttribute ] :
2019-12-18 14:45:14 +01:00
''' Propose a change for an attribute '''
2020-05-07 12:17:31 +02:00
initial_attribute_id = get_uuid_or_id_from_abstract_misp ( initial_attribute )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' shadow_attributes/edit/ { initial_attribute_id } ' , data = attribute )
2020-01-23 10:27:40 +01:00
update_attribute_proposal = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in update_attribute_proposal :
return update_attribute_proposal
a = MISPShadowAttribute ( )
a . from_dict ( * * update_attribute_proposal )
return a
2020-05-07 12:17:31 +02:00
def delete_attribute_proposal ( self , attribute : Union [ MISPAttribute , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Propose the deletion of an attribute '''
2020-05-07 12:17:31 +02:00
attribute_id = get_uuid_or_id_from_abstract_misp ( attribute )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' shadow_attributes/delete/ { attribute_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# NOTE: You cannot modify an existing proposal, only accept/discard
2020-05-07 12:17:31 +02:00
def accept_attribute_proposal ( self , proposal : Union [ MISPShadowAttribute , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Accept a proposal '''
2020-05-07 12:17:31 +02:00
proposal_id = get_uuid_or_id_from_abstract_misp ( proposal )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' shadow_attributes/accept/ { proposal_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def discard_attribute_proposal ( self , proposal : Union [ MISPShadowAttribute , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Discard a proposal '''
2020-05-07 12:17:31 +02:00
proposal_id = get_uuid_or_id_from_abstract_misp ( proposal )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' shadow_attributes/discard/ { proposal_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Attribute Proposal ###
# ## BEGIN Sighting ###
2020-05-07 12:17:31 +02:00
def sightings ( self , misp_entity : Optional [ AbstractMISP ] = None ,
org : Optional [ Union [ MISPOrganisation , int , str , UUID ] ] = None ,
pythonify : bool = False ) - > Union [ Dict , List [ MISPSighting ] ] :
2019-12-18 14:45:14 +01:00
""" Get the list of sighting related to a MISPEvent or a MISPAttribute (depending on type of misp_entity) """
if isinstance ( misp_entity , MISPEvent ) :
2020-01-23 10:27:40 +01:00
url = ' sightings/listSightings '
to_post = { ' context ' : ' event ' , ' id ' : misp_entity . id }
2019-12-18 14:45:14 +01:00
elif isinstance ( misp_entity , MISPAttribute ) :
2020-01-23 10:27:40 +01:00
url = ' sightings/listSightings '
to_post = { ' context ' : ' attribute ' , ' id ' : misp_entity . id }
2019-12-18 14:45:14 +01:00
else :
url = ' sightings '
to_post = { }
2020-01-23 10:27:40 +01:00
if org is not None :
2020-05-07 12:17:31 +02:00
org_id = get_uuid_or_id_from_abstract_misp ( org )
2019-12-18 14:45:14 +01:00
to_post [ ' org_id ' ] = org_id
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , url , data = to_post )
sightings = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in sightings :
return sightings
to_return = [ ]
for sighting in sightings :
s = MISPSighting ( )
s . from_dict ( * * sighting )
to_return . append ( s )
return to_return
2017-02-09 11:59:38 +01:00
2020-05-07 12:17:31 +02:00
def add_sighting ( self , sighting : MISPSighting ,
attribute : Optional [ Union [ MISPAttribute , int , str , UUID ] ] = None ,
pythonify : bool = False ) - > Union [ Dict , MISPSighting ] :
2019-12-18 14:45:14 +01:00
''' Add a new sighting (globally, or to a specific attribute) '''
if attribute :
2020-05-07 12:17:31 +02:00
attribute_id = get_uuid_or_id_from_abstract_misp ( attribute )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , f ' sightings/add/ { attribute_id } ' , data = sighting )
2019-12-18 14:45:14 +01:00
else :
# Either the ID/UUID is in the sighting, or we want to add a sighting on all the attributes with the given value
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' sightings/add ' , data = sighting )
2020-01-23 10:27:40 +01:00
new_sighting = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_sighting :
return new_sighting
s = MISPSighting ( )
s . from_dict ( * * new_sighting )
return s
2017-02-09 11:59:38 +01:00
2020-05-07 12:17:31 +02:00
def delete_sighting ( self , sighting : Union [ MISPSighting , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete a sighting from a MISP instance '''
2020-05-07 12:17:31 +02:00
sighting_id = get_uuid_or_id_from_abstract_misp ( sighting )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' sightings/delete/ { sighting_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-11-22 14:57:11 +01:00
2019-12-18 14:45:14 +01:00
# ## END Sighting ###
2017-11-22 14:57:11 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Tags ###
2017-11-22 14:57:11 +01:00
2020-05-07 12:17:31 +02:00
def tags ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPTag ] ] :
2019-12-18 14:45:14 +01:00
""" Get the list of existing tags. """
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , ' tags ' )
2020-01-23 10:27:40 +01:00
tags = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in tags :
return tags [ ' Tag ' ]
to_return = [ ]
for tag in tags [ ' Tag ' ] :
t = MISPTag ( )
t . from_dict ( * * tag )
to_return . append ( t )
return to_return
2017-11-22 14:57:11 +01:00
2020-05-07 12:17:31 +02:00
def get_tag ( self , tag : Union [ MISPTag , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPTag ] :
2019-12-18 14:45:14 +01:00
""" Get a tag by id. """
2020-05-07 12:17:31 +02:00
tag_id = get_uuid_or_id_from_abstract_misp ( tag )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' tags/view/ { tag_id } ' )
2020-01-23 10:27:40 +01:00
tag_r = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in tag_r :
return tag_r
2019-12-18 14:45:14 +01:00
t = MISPTag ( )
2020-01-23 10:27:40 +01:00
t . from_dict ( * * tag_r )
2019-12-18 14:45:14 +01:00
return t
2020-05-07 12:17:31 +02:00
def add_tag ( self , tag : MISPTag , pythonify : bool = False ) - > Union [ Dict , MISPTag ] :
2019-12-18 14:45:14 +01:00
''' 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.
'''
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' POST ' , ' tags/add ' , data = tag )
2020-01-23 10:27:40 +01:00
new_tag = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_tag :
return new_tag
t = MISPTag ( )
t . from_dict ( * * new_tag )
return t
2020-05-07 12:17:31 +02:00
def enable_tag ( self , tag : MISPTag , pythonify : bool = False ) - > Union [ Dict , MISPTag ] :
2019-12-18 14:45:14 +01:00
""" Enable a tag. """
tag . hide_tag = False
return self . update_tag ( tag , pythonify = pythonify )
2020-05-07 12:17:31 +02:00
def disable_tag ( self , tag : MISPTag , pythonify : bool = False ) - > Union [ Dict , MISPTag ] :
2019-12-18 14:45:14 +01:00
""" Disable a tag. """
tag . hide_tag = True
return self . update_tag ( tag , pythonify = pythonify )
2020-05-07 12:17:31 +02:00
def update_tag ( self , tag : MISPTag , tag_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPTag ] :
2019-12-18 14:45:14 +01:00
""" Edit only the provided parameters of a tag. """
if tag_id is None :
2020-05-07 12:17:31 +02:00
tid = get_uuid_or_id_from_abstract_misp ( tag )
2017-11-02 01:15:49 +01:00
else :
2020-05-07 12:17:31 +02:00
tid = get_uuid_or_id_from_abstract_misp ( tag_id )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' tags/edit/ { tid } ' , data = tag )
updated_tag = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_tag :
return updated_tag
t = MISPTag ( )
t . from_dict ( * * updated_tag )
return t
2020-05-07 12:17:31 +02:00
def delete_tag ( self , tag : Union [ MISPTag , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete an attribute from a MISP instance '''
2020-05-07 12:17:31 +02:00
tag_id = get_uuid_or_id_from_abstract_misp ( tag )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' tags/delete/ { tag_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Tags ###
# ## BEGIN Taxonomies ###
2020-05-07 12:17:31 +02:00
def taxonomies ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPTaxonomy ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the taxonomies. """
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , ' taxonomies ' )
2020-01-23 10:27:40 +01:00
taxonomies = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in taxonomies :
return taxonomies
to_return = [ ]
for taxonomy in taxonomies :
t = MISPTaxonomy ( )
t . from_dict ( * * taxonomy )
to_return . append ( t )
return to_return
2015-08-04 16:24:55 +02:00
2020-05-07 12:17:31 +02:00
def get_taxonomy ( self , taxonomy : Union [ MISPTaxonomy , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPTaxonomy ] :
2019-12-18 14:45:14 +01:00
""" Get a taxonomy from a MISP instance. """
2020-05-07 12:17:31 +02:00
taxonomy_id = get_uuid_or_id_from_abstract_misp ( taxonomy )
2020-01-03 15:42:15 +01:00
r = self . _prepare_request ( ' GET ' , f ' taxonomies/view/ { taxonomy_id } ' )
2020-01-23 10:27:40 +01:00
taxonomy_r = self . _check_json_response ( r )
2020-01-03 15:42:15 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in taxonomy_r :
return taxonomy_r
2019-12-18 14:45:14 +01:00
t = MISPTaxonomy ( )
2020-01-03 15:42:15 +01:00
t . from_dict ( * * taxonomy_r )
2019-12-18 14:45:14 +01:00
return t
2020-05-07 12:17:31 +02:00
def enable_taxonomy ( self , taxonomy : Union [ MISPTaxonomy , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Enable a taxonomy. """
2020-05-07 12:17:31 +02:00
taxonomy_id = get_uuid_or_id_from_abstract_misp ( taxonomy )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' taxonomies/enable/ { taxonomy_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def disable_taxonomy ( self , taxonomy : Union [ MISPTaxonomy , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Disable a taxonomy. """
2020-05-07 12:17:31 +02:00
taxonomy_id = get_uuid_or_id_from_abstract_misp ( taxonomy )
2019-12-18 14:45:14 +01:00
self . disable_taxonomy_tags ( taxonomy_id )
response = self . _prepare_request ( ' POST ' , f ' taxonomies/disable/ { taxonomy_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def disable_taxonomy_tags ( self , taxonomy : Union [ MISPTaxonomy , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Disable all the tags of a taxonomy. """
2020-05-07 12:17:31 +02:00
taxonomy_id = get_uuid_or_id_from_abstract_misp ( taxonomy )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' taxonomies/disableTag/ { taxonomy_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def enable_taxonomy_tags ( self , taxonomy : Union [ MISPTaxonomy , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Enable all the tags of a taxonomy.
NOTE : this automatically done when you call enable_taxonomy . """
2020-05-07 12:17:31 +02:00
taxonomy_id = get_uuid_or_id_from_abstract_misp ( taxonomy )
2020-01-23 10:27:40 +01:00
t = self . get_taxonomy ( taxonomy_id )
if not t [ ' Taxonomy ' ] [ ' enabled ' ] :
raise PyMISPError ( f " The taxonomy { t [ ' Taxonomy ' ] [ ' name ' ] } is not enabled. " )
2019-12-18 14:45:14 +01:00
url = urljoin ( self . root_url , ' taxonomies/addTag/ {} ' . format ( taxonomy_id ) )
response = self . _prepare_request ( ' POST ' , url )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2015-10-30 17:23:25 +01:00
2020-05-07 12:17:31 +02:00
def update_taxonomies ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Update all the taxonomies. """
response = self . _prepare_request ( ' POST ' , ' taxonomies/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-06-13 15:37:39 +02:00
2019-12-18 14:45:14 +01:00
# ## END Taxonomies ###
2014-04-11 18:45:52 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Warninglists ###
2017-05-03 11:13:18 +02:00
2020-05-07 12:17:31 +02:00
def warninglists ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPWarninglist ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the warninglists. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' warninglists ' )
warninglists = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in warninglists :
return warninglists [ ' Warninglists ' ]
2017-12-14 11:06:52 +01:00
to_return = [ ]
2019-12-18 14:45:14 +01:00
for warninglist in warninglists [ ' Warninglists ' ] :
w = MISPWarninglist ( )
w . from_dict ( * * warninglist )
to_return . append ( w )
2014-04-11 18:45:52 +02:00
return to_return
2020-05-07 12:17:31 +02:00
def get_warninglist ( self , warninglist : Union [ MISPWarninglist , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPWarninglist ] :
2019-12-18 14:45:14 +01:00
""" Get a warninglist. """
2020-05-07 12:17:31 +02:00
warninglist_id = get_uuid_or_id_from_abstract_misp ( warninglist )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' warninglists/view/ { warninglist_id } ' )
wl = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in wl :
return wl
2019-12-18 14:45:14 +01:00
w = MISPWarninglist ( )
2020-01-23 10:27:40 +01:00
w . from_dict ( * * wl )
2019-12-18 14:45:14 +01:00
return w
2020-05-07 12:17:31 +02:00
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 :
2019-12-18 14:45:14 +01:00
''' 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 )
'''
if warninglist_id is None and warninglist_name is None :
raise PyMISPError ( ' Either warninglist_id or warninglist_name is required. ' )
2020-01-23 10:27:40 +01:00
query : Dict [ str , Union [ List [ str ] , List [ int ] , bool ] ] = { }
2019-12-18 14:45:14 +01:00
if warninglist_id is not None :
2020-01-23 10:27:40 +01:00
if isinstance ( warninglist_id , list ) :
query [ ' id ' ] = warninglist_id
else :
query [ ' id ' ] = [ warninglist_id ] # type: ignore
2019-12-18 14:45:14 +01:00
if warninglist_name is not None :
2020-01-23 10:27:40 +01:00
if isinstance ( warninglist_name , list ) :
query [ ' name ' ] = warninglist_name
else :
query [ ' name ' ] = [ warninglist_name ]
2019-12-18 14:45:14 +01:00
if force_enable :
query [ ' enabled ' ] = force_enable
2020-01-23 10:27:40 +01:00
response = self . _prepare_request ( ' POST ' , ' warninglists/toggleEnable ' , data = query )
return self . _check_json_response ( response )
2017-01-24 09:41:30 +01:00
2020-05-07 12:17:31 +02:00
def enable_warninglist ( self , warninglist : Union [ MISPWarninglist , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Enable a warninglist. """
2020-05-07 12:17:31 +02:00
warninglist_id = get_uuid_or_id_from_abstract_misp ( warninglist )
2019-12-18 14:45:14 +01:00
return self . toggle_warninglist ( warninglist_id = warninglist_id , force_enable = True )
2017-01-24 09:41:30 +01:00
2020-05-07 12:17:31 +02:00
def disable_warninglist ( self , warninglist : Union [ MISPWarninglist , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Disable a warninglist. """
2020-05-07 12:17:31 +02:00
warninglist_id = get_uuid_or_id_from_abstract_misp ( warninglist )
2019-12-18 14:45:14 +01:00
return self . toggle_warninglist ( warninglist_id = warninglist_id , force_enable = False )
2017-01-24 09:41:30 +01:00
2020-05-07 12:17:31 +02:00
def values_in_warninglist ( self , value : Iterator ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Check if IOC values are in warninglist """
2020-01-23 10:27:40 +01:00
response = self . _prepare_request ( ' POST ' , ' warninglists/checkValue ' , data = value )
return self . _check_json_response ( response )
2014-04-11 18:45:52 +02:00
2020-05-07 12:17:31 +02:00
def update_warninglists ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Update all the warninglists. """
response = self . _prepare_request ( ' POST ' , ' warninglists/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2015-08-07 17:24:03 +02:00
2019-12-18 14:45:14 +01:00
# ## END Warninglists ###
2015-08-05 17:20:59 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Noticelist ###
2015-08-05 17:20:59 +02:00
2020-05-07 12:17:31 +02:00
def noticelists ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPNoticelist ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the noticelists. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' noticelists ' )
noticelists = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in noticelists :
return noticelists
to_return = [ ]
for noticelist in noticelists :
n = MISPNoticelist ( )
n . from_dict ( * * noticelist )
to_return . append ( n )
return to_return
2017-12-11 15:01:25 +01:00
2020-05-07 12:17:31 +02:00
def get_noticelist ( self , noticelist : Union [ MISPNoticelist , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPNoticelist ] :
2019-12-18 14:45:14 +01:00
""" Get a noticelist by id. """
2020-05-07 12:17:31 +02:00
noticelist_id = get_uuid_or_id_from_abstract_misp ( noticelist )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' noticelists/view/ { noticelist_id } ' )
noticelist_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in noticelist_j :
return noticelist_j
2019-12-18 14:45:14 +01:00
n = MISPNoticelist ( )
2020-01-23 10:27:40 +01:00
n . from_dict ( * * noticelist_j )
2019-12-18 14:45:14 +01:00
return n
2020-05-07 12:17:31 +02:00
def enable_noticelist ( self , noticelist : Union [ MISPNoticelist , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Enable a noticelist by id. """
# FIXME: https://github.com/MISP/MISP/issues/4856
# response = self._prepare_request('POST', f'noticelists/enable/{noticelist_id}')
2020-05-07 12:17:31 +02:00
noticelist_id = get_uuid_or_id_from_abstract_misp ( noticelist )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' noticelists/enableNoticelist/ { noticelist_id } /true ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-11-29 16:46:41 +01:00
2020-05-07 12:17:31 +02:00
def disable_noticelist ( self , noticelist : Union [ MISPNoticelist , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Disable a noticelist by id. """
# FIXME: https://github.com/MISP/MISP/issues/4856
# response = self._prepare_request('POST', f'noticelists/disable/{noticelist_id}')
2020-05-07 12:17:31 +02:00
noticelist_id = get_uuid_or_id_from_abstract_misp ( noticelist )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' noticelists/enableNoticelist/ { noticelist_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-11-29 16:46:41 +01:00
2020-05-07 12:17:31 +02:00
def update_noticelists ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Update all the noticelists. """
response = self . _prepare_request ( ' POST ' , ' noticelists/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-12-11 15:01:25 +01:00
2019-12-18 14:45:14 +01:00
# ## END Noticelist ###
2017-12-11 15:01:25 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Galaxy ###
2017-11-29 16:46:41 +01:00
2020-05-07 12:17:31 +02:00
def galaxies ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPGalaxy ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the galaxies. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' galaxies ' )
galaxies = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in galaxies :
return galaxies
to_return = [ ]
for galaxy in galaxies :
g = MISPGalaxy ( )
g . from_dict ( * * galaxy )
to_return . append ( g )
return to_return
2015-12-21 18:58:08 +01:00
2020-05-07 12:17:31 +02:00
def get_galaxy ( self , galaxy : Union [ MISPGalaxy , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPGalaxy ] :
2019-12-18 14:45:14 +01:00
""" Get a galaxy by id. """
2020-05-07 12:17:31 +02:00
galaxy_id = get_uuid_or_id_from_abstract_misp ( galaxy )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' galaxies/view/ { galaxy_id } ' )
galaxy_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in galaxy_j :
return galaxy_j
2019-12-18 14:45:14 +01:00
g = MISPGalaxy ( )
2020-01-23 10:27:40 +01:00
g . from_dict ( * * galaxy_j )
2019-12-18 14:45:14 +01:00
return g
2015-12-21 18:58:08 +01:00
2020-05-07 12:17:31 +02:00
def update_galaxies ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Update all the galaxies. """
response = self . _prepare_request ( ' POST ' , ' galaxies/update ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2016-03-14 12:17:53 +01:00
2019-12-18 14:45:14 +01:00
# ## END Galaxy ###
2015-09-17 13:51:31 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Feed ###
2015-09-18 12:03:56 +02:00
2020-05-07 12:17:31 +02:00
def feeds ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPFeed ] ] :
2019-12-18 14:45:14 +01:00
""" Get the list of existing feeds. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' feeds ' )
feeds = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in feeds :
return feeds
to_return = [ ]
for feed in feeds :
f = MISPFeed ( )
f . from_dict ( * * feed )
to_return . append ( f )
return to_return
2020-05-07 12:17:31 +02:00
def get_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
""" Get a feed by id. """
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' feeds/view/ { feed_id } ' )
feed_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in feed_j :
return feed_j
2019-12-18 14:45:14 +01:00
f = MISPFeed ( )
2020-01-23 10:27:40 +01:00
f . from_dict ( * * feed_j )
2019-12-18 14:45:14 +01:00
return f
2020-05-07 12:17:31 +02:00
def add_feed ( self , feed : MISPFeed , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Add a new feed on a MISP instance '''
# FIXME: https://github.com/MISP/MISP/issues/4834
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , ' feeds/add ' , data = { ' Feed ' : feed } )
new_feed = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_feed :
return new_feed
f = MISPFeed ( )
f . from_dict ( * * new_feed )
return f
2020-05-07 12:17:31 +02:00
def enable_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Enable a feed (fetching it will create event(s) '''
if not isinstance ( feed , MISPFeed ) :
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed ) # In case we have a UUID
2020-01-23 10:27:40 +01:00
f = MISPFeed ( )
f . id = feed_id
f . enabled = True
2020-05-12 11:24:47 +02:00
else :
f = feed
2020-01-23 10:27:40 +01:00
return self . update_feed ( feed = f , pythonify = pythonify )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def disable_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Disable a feed '''
if not isinstance ( feed , MISPFeed ) :
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed ) # In case we have a UUID
2020-01-23 10:27:40 +01:00
f = MISPFeed ( )
f . id = feed_id
f . enabled = False
2020-05-12 11:24:47 +02:00
else :
f = feed
2020-01-23 10:27:40 +01:00
return self . update_feed ( feed = f , pythonify = pythonify )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def enable_feed_cache ( self , feed : Union [ MISPFeed , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Enable the caching of a feed '''
if not isinstance ( feed , MISPFeed ) :
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed ) # In case we have a UUID
2020-01-23 10:27:40 +01:00
f = MISPFeed ( )
f . id = feed_id
f . caching_enabled = True
2020-05-12 11:24:47 +02:00
else :
f = feed
2020-01-23 10:27:40 +01:00
return self . update_feed ( feed = f , pythonify = pythonify )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def disable_feed_cache ( self , feed : Union [ MISPFeed , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Disable the caching of a feed '''
if not isinstance ( feed , MISPFeed ) :
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed ) # In case we have a UUID
2020-01-23 10:27:40 +01:00
f = MISPFeed ( )
f . id = feed_id
f . caching_enabled = False
2020-05-12 11:24:47 +02:00
else :
f = feed
2020-01-23 10:27:40 +01:00
return self . update_feed ( feed = f , pythonify = pythonify )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def update_feed ( self , feed : MISPFeed , feed_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPFeed ] :
2019-12-18 14:45:14 +01:00
''' Update a feed on a MISP instance '''
if feed_id is None :
2020-05-07 12:17:31 +02:00
fid = get_uuid_or_id_from_abstract_misp ( feed )
2015-09-18 12:03:56 +02:00
else :
2020-05-07 12:17:31 +02:00
fid = get_uuid_or_id_from_abstract_misp ( feed_id )
2019-12-18 14:45:14 +01:00
# FIXME: https://github.com/MISP/MISP/issues/4834
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' feeds/edit/ { fid } ' , data = { ' Feed ' : feed } )
updated_feed = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_feed :
return updated_feed
f = MISPFeed ( )
f . from_dict ( * * updated_feed )
return f
2020-05-07 12:17:31 +02:00
def delete_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete a feed from a MISP instance '''
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' feeds/delete/ { feed_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def fetch_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Fetch one single feed """
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' GET ' , f ' feeds/fetchFromFeed/ { feed_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2015-09-18 12:03:56 +02:00
2020-05-07 12:17:31 +02:00
def cache_all_feeds ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Cache all the feeds """
response = self . _prepare_request ( ' GET ' , ' feeds/cacheFeeds/all ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-01-27 11:58:00 +01:00
2020-05-07 12:17:31 +02:00
def cache_feed ( self , feed : Union [ MISPFeed , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Cache a specific feed """
2020-05-07 12:17:31 +02:00
feed_id = get_uuid_or_id_from_abstract_misp ( feed )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' GET ' , f ' feeds/cacheFeeds/ { feed_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2015-09-17 13:51:31 +02:00
2020-05-07 12:17:31 +02:00
def cache_freetext_feeds ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Cache all the freetext feeds """
response = self . _prepare_request ( ' GET ' , ' feeds/cacheFeeds/freetext ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2018-08-13 12:10:15 +02:00
2020-05-07 12:17:31 +02:00
def cache_misp_feeds ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Cache all the MISP feeds """
response = self . _prepare_request ( ' GET ' , ' feeds/cacheFeeds/misp ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2016-04-29 16:35:27 +02:00
2020-05-07 12:17:31 +02:00
def compare_feeds ( self ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Generate the comparison matrix for all the MISP feeds """
response = self . _prepare_request ( ' GET ' , ' feeds/compareFeeds ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2016-04-29 16:35:27 +02:00
2020-05-12 11:24:47 +02:00
def load_default_feeds ( self ) - > Dict :
""" Load all the default feeds. """
response = self . _prepare_request ( ' POST ' , ' feeds/loadDefaultFeeds ' )
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Feed ###
2016-04-29 16:35:27 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Server ###
2018-03-26 17:03:16 +02:00
2020-05-07 12:17:31 +02:00
def servers ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPServer ] ] :
2019-12-18 14:45:14 +01:00
""" Get the existing servers the MISP instance can synchronise with """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' servers ' )
servers = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in servers :
return servers
to_return = [ ]
for server in servers :
s = MISPServer ( )
s . from_dict ( * * server )
to_return . append ( s )
return to_return
2018-03-26 17:03:16 +02:00
2020-05-07 12:17:31 +02:00
def get_sync_config ( self , pythonify : bool = False ) - > Union [ Dict , MISPServer ] :
2019-12-18 14:45:14 +01:00
''' WARNING: This method only works if the user calling it is a sync user '''
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' servers/createSync ' )
server = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in server :
return server
s = MISPServer ( )
s . from_dict ( * * server )
return s
2020-05-07 12:17:31 +02:00
def import_server ( self , server : MISPServer , pythonify : bool = False ) - > Union [ Dict , MISPServer ] :
2019-12-18 14:45:14 +01:00
""" Import a sync server config received from get_sync_config """
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' servers/import ' , data = server )
2020-01-23 10:27:40 +01:00
server_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in server_j :
return server_j
2019-12-18 14:45:14 +01:00
s = MISPServer ( )
2020-01-23 10:27:40 +01:00
s . from_dict ( * * server_j )
2019-12-18 14:45:14 +01:00
return s
2020-05-07 12:17:31 +02:00
def add_server ( self , server : MISPServer , pythonify : bool = False ) - > Union [ Dict , MISPServer ] :
2019-12-18 14:45:14 +01:00
""" Add a server to synchronise with.
2020-01-15 17:26:08 +01:00
Note : You probably want to use ExpandedPyMISP . get_sync_config and ExpandedPyMISP . import_server instead """
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' servers/add ' , data = server )
2020-01-23 10:27:40 +01:00
server_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in server_j :
return server_j
2019-12-18 14:45:14 +01:00
s = MISPServer ( )
2020-01-23 10:27:40 +01:00
s . from_dict ( * * server_j )
2019-12-18 14:45:14 +01:00
return s
2020-05-07 12:17:31 +02:00
def update_server ( self , server : MISPServer , server_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPServer ] :
2019-12-18 14:45:14 +01:00
''' Update a server to synchronise with '''
if server_id is None :
2020-05-07 12:17:31 +02:00
sid = get_uuid_or_id_from_abstract_misp ( server )
2018-02-21 15:12:26 +01:00
else :
2020-05-07 12:17:31 +02:00
sid = get_uuid_or_id_from_abstract_misp ( server_id )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' servers/edit/ { sid } ' , data = server )
updated_server = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_server :
return updated_server
s = MISPServer ( )
s . from_dict ( * * updated_server )
return s
2020-05-07 12:17:31 +02:00
def delete_server ( self , server : Union [ MISPServer , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete a sync server '''
2020-05-07 12:17:31 +02:00
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' servers/delete/ { server_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def server_pull ( self , server : Union [ MISPServer , int , str , UUID ] , event : Optional [ Union [ MISPEvent , int , str , UUID ] ] = None ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Initialize a pull from a sync server '''
2020-05-07 12:17:31 +02:00
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
if event :
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
url = f ' servers/pull/ { server_id } / { event_id } '
else :
url = f ' servers/pull/ { server_id } '
2018-08-09 18:11:45 +02:00
response = self . _prepare_request ( ' GET ' , url )
2019-12-18 14:45:14 +01:00
# FIXME: can we pythonify?
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2016-11-03 11:23:48 +01:00
2020-05-07 12:17:31 +02:00
def server_push ( self , server : Union [ MISPServer , int , str , UUID ] , event : Optional [ Union [ MISPEvent , int , str , UUID ] ] = None ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Initialize a push to a sync server '''
2020-05-07 12:17:31 +02:00
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
if event :
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
url = f ' servers/push/ { server_id } / { event_id } '
2017-04-07 17:10:47 +02:00
else :
2019-12-18 14:45:14 +01:00
url = f ' servers/push/ { server_id } '
response = self . _prepare_request ( ' GET ' , url )
# FIXME: can we pythonify?
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-02-22 17:22:00 +01:00
2020-05-07 12:17:31 +02:00
def test_server ( self , server : Union [ MISPServer , int , str , UUID ] ) - > Dict :
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' servers/testConnection/ { server_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-02-23 14:24:01 +01:00
2019-12-18 14:45:14 +01:00
# ## END Server ###
2017-02-23 14:24:01 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Sharing group ###
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def sharing_groups ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPSharingGroup ] ] :
2019-12-18 14:45:14 +01:00
""" Get the existing sharing groups """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' sharing_groups ' )
sharing_groups = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in sharing_groups :
return sharing_groups
to_return = [ ]
for sharing_group in sharing_groups :
s = MISPSharingGroup ( )
s . from_dict ( * * sharing_group )
to_return . append ( s )
return to_return
2018-08-27 05:41:51 +02:00
2020-05-07 12:17:31 +02:00
def add_sharing_group ( self , sharing_group : MISPSharingGroup , pythonify : bool = False ) - > Union [ Dict , MISPSharingGroup ] :
2019-12-18 14:45:14 +01:00
""" Add a new sharing group """
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' sharing_groups/add ' , data = sharing_group )
2020-01-23 10:27:40 +01:00
sharing_group_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in sharing_group_j :
return sharing_group_j
2019-12-18 14:45:14 +01:00
s = MISPSharingGroup ( )
2020-01-23 10:27:40 +01:00
s . from_dict ( * * sharing_group_j )
2019-12-18 14:45:14 +01:00
return s
2020-05-07 12:17:31 +02:00
def delete_sharing_group ( self , sharing_group : Union [ MISPSharingGroup , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Delete a sharing group """
2020-05-07 12:17:31 +02:00
sharing_group_id = get_uuid_or_id_from_abstract_misp ( sharing_group )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' sharing_groups/delete/ { sharing_group_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
def add_org_to_sharing_group ( self , sharing_group : Union [ MISPSharingGroup , int , str , UUID ] ,
2020-05-07 12:17:31 +02:00
organisation : Union [ MISPOrganisation , int , str , UUID ] , extend : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
''' 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
'''
2020-05-07 12:17:31 +02:00
sharing_group_id = get_uuid_or_id_from_abstract_misp ( sharing_group )
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
2019-12-18 14:45:14 +01:00
to_jsonify = { ' sg_id ' : sharing_group_id , ' org_id ' : organisation_id , ' extend ' : extend }
response = self . _prepare_request ( ' POST ' , ' sharingGroups/addOrg ' , data = to_jsonify )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2018-08-27 05:41:51 +02:00
2019-12-18 14:45:14 +01:00
def remove_org_from_sharing_group ( self , sharing_group : Union [ MISPSharingGroup , int , str , UUID ] ,
2020-05-07 12:17:31 +02:00
organisation : Union [ MISPOrganisation , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' 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
'''
2020-05-07 12:17:31 +02:00
sharing_group_id = get_uuid_or_id_from_abstract_misp ( sharing_group )
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
2019-12-18 14:45:14 +01:00
to_jsonify = { ' sg_id ' : sharing_group_id , ' org_id ' : organisation_id }
response = self . _prepare_request ( ' POST ' , ' sharingGroups/removeOrg ' , data = to_jsonify )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2018-08-27 05:41:51 +02:00
2019-12-18 14:45:14 +01:00
def add_server_to_sharing_group ( self , sharing_group : Union [ MISPSharingGroup , int , str , UUID ] ,
2020-05-07 12:17:31 +02:00
server : Union [ MISPServer , int , str , UUID ] , all_orgs : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
''' 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
'''
2020-05-07 12:17:31 +02:00
sharing_group_id = get_uuid_or_id_from_abstract_misp ( sharing_group )
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
to_jsonify = { ' sg_id ' : sharing_group_id , ' server_id ' : server_id , ' all_orgs ' : all_orgs }
response = self . _prepare_request ( ' POST ' , ' sharingGroups/addServer ' , data = to_jsonify )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-07-15 20:35:58 +02:00
2019-12-18 14:45:14 +01:00
def remove_server_from_sharing_group ( self , sharing_group : Union [ MISPSharingGroup , int , str , UUID ] ,
2020-05-07 12:17:31 +02:00
server : Union [ MISPServer , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' 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
'''
2020-05-07 12:17:31 +02:00
sharing_group_id = get_uuid_or_id_from_abstract_misp ( sharing_group )
server_id = get_uuid_or_id_from_abstract_misp ( server )
2019-12-18 14:45:14 +01:00
to_jsonify = { ' sg_id ' : sharing_group_id , ' server_id ' : server_id }
response = self . _prepare_request ( ' POST ' , ' sharingGroups/removeServer ' , data = to_jsonify )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
# ## END Sharing groups ###
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Organisation ###
2018-10-04 19:03:24 +02:00
2020-05-07 12:17:31 +02:00
def organisations ( self , scope = " local " , pythonify : bool = False ) - > Union [ Dict , List [ MISPOrganisation ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the organisations. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' organisations/index/scope: { scope } ' )
organisations = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in organisations :
return organisations
to_return = [ ]
for organisation in organisations :
o = MISPOrganisation ( )
o . from_dict ( * * organisation )
to_return . append ( o )
return to_return
2018-10-04 19:03:24 +02:00
2020-05-07 12:17:31 +02:00
def get_organisation ( self , organisation : Union [ MISPOrganisation , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPOrganisation ] :
2019-12-18 14:45:14 +01:00
''' Get an organisation. '''
2020-05-07 12:17:31 +02:00
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' organisations/view/ { organisation_id } ' )
organisation_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in organisation_j :
return organisation_j
2019-12-18 14:45:14 +01:00
o = MISPOrganisation ( )
2020-01-23 10:27:40 +01:00
o . from_dict ( * * organisation_j )
2019-12-18 14:45:14 +01:00
return o
2020-05-07 12:17:31 +02:00
def add_organisation ( self , organisation : MISPOrganisation , pythonify : bool = False ) - > Union [ Dict , MISPOrganisation ] :
2019-12-18 14:45:14 +01:00
''' Add an organisation '''
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' admin/organisations/add ' , data = organisation )
2020-01-23 10:27:40 +01:00
new_organisation = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in new_organisation :
return new_organisation
o = MISPOrganisation ( )
o . from_dict ( * * new_organisation )
return o
2020-05-07 12:17:31 +02:00
def update_organisation ( self , organisation : MISPOrganisation , organisation_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPOrganisation ] :
2019-12-18 14:45:14 +01:00
''' Update an organisation '''
if organisation_id is None :
2020-05-07 12:17:31 +02:00
oid = get_uuid_or_id_from_abstract_misp ( organisation )
2019-12-18 14:45:14 +01:00
else :
2020-05-07 12:17:31 +02:00
oid = get_uuid_or_id_from_abstract_misp ( organisation_id )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' admin/organisations/edit/ { oid } ' , data = organisation )
updated_organisation = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_organisation :
return updated_organisation
o = MISPOrganisation ( )
o . from_dict ( * * organisation )
return o
2020-05-07 12:17:31 +02:00
def delete_organisation ( self , organisation : Union [ MISPOrganisation , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete an organisation '''
# NOTE: MISP in inconsistent and currently require "delete" in the path and doesn't support HTTP DELETE
2020-05-07 12:17:31 +02:00
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' admin/organisations/delete/ { organisation_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Organisation ###
# ## BEGIN User ###
2020-05-07 12:17:31 +02:00
def users ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPUser ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the users. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' admin/users ' )
users = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in users :
return users
to_return = [ ]
for user in users :
u = MISPUser ( )
u . from_dict ( * * user )
to_return . append ( u )
return to_return
2018-10-04 19:03:24 +02:00
2020-05-07 12:17:31 +02:00
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 ] ] ] :
2019-12-18 14:45:14 +01:00
''' Get a user. `me` means the owner of the API key doing the query.
expanded also returns a MISPRole and a MISPUserSetting '''
2020-05-07 12:17:31 +02:00
user_id = get_uuid_or_id_from_abstract_misp ( user )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' users/view/ { user_id } ' )
user_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in user_j :
return user_j
2019-12-18 14:45:14 +01:00
u = MISPUser ( )
2020-01-23 10:27:40 +01:00
u . from_dict ( * * user_j )
2019-12-18 14:45:14 +01:00
if not expanded :
return u
else :
2020-01-23 10:27:40 +01:00
role = MISPRole ( )
role . from_dict ( * * user_j [ ' Role ' ] )
2019-12-18 14:45:14 +01:00
usersettings = [ ]
2020-01-23 10:27:40 +01:00
if user_j [ ' UserSetting ' ] :
for name , value in user_j [ ' UserSetting ' ] . items ( ) :
2019-12-18 14:45:14 +01:00
us = MISPUserSetting ( )
us . from_dict ( * * { ' name ' : name , ' value ' : value } )
usersettings . append ( us )
2020-01-23 10:27:40 +01:00
return u , role , usersettings
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def add_user ( self , user : MISPUser , pythonify : bool = False ) - > Union [ Dict , MISPUser ] :
2019-12-18 14:45:14 +01:00
''' Add a new user '''
2020-05-12 11:34:38 +02:00
r = self . _prepare_request ( ' POST ' , ' admin/users/add ' , data = user )
2020-01-23 10:27:40 +01:00
user_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in user_j :
return user_j
2019-12-18 14:45:14 +01:00
u = MISPUser ( )
2020-01-23 10:27:40 +01:00
u . from_dict ( * * user_j )
2019-12-18 14:45:14 +01:00
return u
2020-05-07 12:17:31 +02:00
def update_user ( self , user : MISPUser , user_id : Optional [ int ] = None , pythonify : bool = False ) - > Union [ Dict , MISPUser ] :
2019-12-18 14:45:14 +01:00
''' Update an event on a MISP instance '''
if user_id is None :
2020-05-07 12:17:31 +02:00
uid = get_uuid_or_id_from_abstract_misp ( user )
2019-12-18 14:45:14 +01:00
else :
2020-05-07 12:17:31 +02:00
uid = get_uuid_or_id_from_abstract_misp ( user_id )
2020-01-23 10:27:40 +01:00
url = f ' users/edit/ { uid } '
2019-12-18 14:45:14 +01:00
if self . _current_role . perm_admin or self . _current_role . perm_site_admin :
url = f ' admin/ { url } '
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , url , data = user )
updated_user = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in updated_user :
return updated_user
e = MISPUser ( )
e . from_dict ( * * updated_user )
return e
2017-12-13 16:43:21 +01:00
2020-05-07 12:17:31 +02:00
def delete_user ( self , user : Union [ MISPUser , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete a user '''
# NOTE: MISP in inconsistent and currently require "delete" in the path and doesn't support HTTP DELETE
2020-05-07 12:17:31 +02:00
user_id = get_uuid_or_id_from_abstract_misp ( user )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' admin/users/delete/ { user_id } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-12-13 16:43:21 +01:00
2020-06-02 10:08:17 +02:00
def change_user_password ( self , new_password : str ) - > Dict :
''' Thange the password of the curent user '''
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' POST ' , ' users/change_pw ' , data = { ' password ' : new_password } )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-12-13 16:43:21 +01:00
2020-05-07 12:17:31 +02:00
def user_registrations ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPInbox ] ] :
""" Get all the user registrations. """
r = self . _prepare_request ( ' GET ' , ' users/registrations ' )
registrations = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in registrations :
return registrations
to_return = [ ]
for registration in registrations :
i = MISPInbox ( )
i . from_dict ( * * registration )
to_return . append ( i )
return to_return
def accept_user_registration ( self , registration : Union [ MISPInbox , int , str , UUID ] ,
organisation : Optional [ Union [ MISPOrganisation , int , str , UUID ] ] = None ,
role : Optional [ Union [ MISPRole , int , str ] ] = None ,
perm_sync : bool = False , perm_publish : bool = False , perm_admin : bool = False ,
unsafe_fallback : bool = False ) :
registration_id = get_uuid_or_id_from_abstract_misp ( registration )
if role :
role_id = role_id = get_uuid_or_id_from_abstract_misp ( role )
else :
for role in self . roles ( pythonify = True ) :
if not isinstance ( role , MISPRole ) :
continue
if role . default_role : # type: ignore
role_id = get_uuid_or_id_from_abstract_misp ( role )
break
else :
raise PyMISPError ( ' Unable to find default role ' )
organisation_id = None
if organisation :
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
elif unsafe_fallback and isinstance ( registration , MISPInbox ) :
if ' org_uuid ' in registration . data :
org = self . get_organisation ( registration . data [ ' org_uuid ' ] , pythonify = True )
if isinstance ( org , MISPOrganisation ) :
organisation_id = org . id
if unsafe_fallback and isinstance ( registration , MISPInbox ) :
# Blindly use request from user, and instance defaults.
to_post = { ' User ' : { ' org_id ' : organisation_id , ' role_id ' : role_id ,
' perm_sync ' : registration . data [ ' perm_sync ' ] ,
' perm_publish ' : registration . data [ ' perm_publish ' ] ,
' perm_admin ' : registration . data [ ' perm_admin ' ] } }
else :
to_post = { ' User ' : { ' org_id ' : organisation_id , ' role_id ' : role_id ,
' perm_sync ' : perm_sync , ' perm_publish ' : perm_publish ,
' perm_admin ' : perm_admin } }
r = self . _prepare_request ( ' POST ' , f ' users/acceptRegistrations/ { registration_id } ' , data = to_post )
return self . _check_json_response ( r )
def discard_user_registration ( self , registration : Union [ MISPInbox , int , str , UUID ] ) :
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 )
2019-12-18 14:45:14 +01:00
# ## END User ###
2018-09-28 18:14:27 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Role ###
2018-10-04 19:03:24 +02:00
2020-05-07 12:17:31 +02:00
def roles ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPRole ] ] :
2019-12-18 14:45:14 +01:00
""" Get the existing roles """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' roles ' )
roles = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in roles :
return roles
to_return = [ ]
for role in roles :
2020-01-23 10:27:40 +01:00
nr = MISPRole ( )
nr . from_dict ( * * role )
to_return . append ( nr )
2019-12-18 14:45:14 +01:00
return to_return
2018-10-04 19:03:24 +02:00
2020-05-07 12:17:31 +02:00
def set_default_role ( self , role : Union [ MISPRole , int , str , UUID ] ) - > Dict :
role_id = get_uuid_or_id_from_abstract_misp ( role )
2019-12-18 14:45:14 +01:00
url = urljoin ( self . root_url , f ' /admin/roles/set_default/ { role_id } ' )
2018-10-04 19:03:24 +02:00
response = self . _prepare_request ( ' POST ' , url )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END Role ###
# ## BEGIN Search methods ###
def search ( self , controller : str = ' events ' , return_format : str = ' json ' ,
limit : Optional [ int ] = None , page : Optional [ int ] = None ,
value : Optional [ SearchParameterTypes ] = None ,
type_attribute : Optional [ SearchParameterTypes ] = None ,
category : Optional [ SearchParameterTypes ] = None ,
org : Optional [ SearchParameterTypes ] = None ,
tags : Optional [ SearchParameterTypes ] = None ,
quick_filter : Optional [ str ] = None , quickFilter : Optional [ str ] = None ,
2020-01-23 10:27:40 +01:00
date_from : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
date_to : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
2019-12-18 14:45:14 +01:00
eventid : Optional [ SearchType ] = None ,
with_attachments : Optional [ bool ] = None , withAttachments : Optional [ bool ] = None ,
metadata : Optional [ bool ] = None ,
uuid : Optional [ str ] = None ,
2020-01-23 10:27:40 +01:00
publish_timestamp : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2020-01-23 10:27:40 +01:00
last : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2020-01-23 10:27:40 +01:00
timestamp : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2019-12-18 14:45:14 +01:00
published : Optional [ bool ] = None ,
enforce_warninglist : Optional [ bool ] = None , enforceWarninglist : Optional [ bool ] = None ,
to_ids : Optional [ Union [ ToIDSType , List [ ToIDSType ] ] ] = None ,
deleted : Optional [ str ] = None ,
include_event_uuid : Optional [ bool ] = None , includeEventUuid : Optional [ bool ] = None ,
include_event_tags : Optional [ bool ] = None , includeEventTags : Optional [ bool ] = None ,
2020-01-23 10:27:40 +01:00
event_timestamp : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
2019-12-18 14:45:14 +01:00
sg_reference_only : Optional [ bool ] = None ,
eventinfo : Optional [ str ] = None ,
searchall : Optional [ bool ] = None ,
requested_attributes : Optional [ str ] = None ,
include_context : Optional [ bool ] = None , includeContext : Optional [ bool ] = None ,
headerless : Optional [ bool ] = None ,
include_sightings : Optional [ bool ] = None , includeSightings : Optional [ bool ] = None ,
include_correlations : Optional [ bool ] = None , includeCorrelations : Optional [ bool ] = None ,
2020-01-30 14:05:02 +01:00
include_decay_score : Optional [ bool ] = None , includeDecayScore : Optional [ bool ] = None ,
2020-05-07 13:59:45 +02:00
object_name : Optional [ str ] = None ,
2019-12-18 14:45:14 +01:00
pythonify : Optional [ bool ] = False ,
2020-05-07 12:17:31 +02:00
* * kwargs ) - > Union [ Dict , str , List [ Union [ MISPEvent , MISPAttribute , MISPObject ] ] ] :
2019-12-18 14:45:14 +01:00
''' Search in the MISP instance
: param return_format : Set the return format of the search ( Currently supported : json , xml , openioc , suricata , snort - more formats are being moved to restSearch with the goal being that all searches happen through this API ) . Can be passed as the first parameter after restSearch or via the JSON payload .
: param limit : Limit the number of results returned , depending on the scope ( for example 10 attributes or 10 full events ) .
: param page : If a limit is set , sets the page to be returned . page 3 , limit 100 will return records 201 - > 300 ) .
: param value : Search for the given value in the attributes ' value field.
: param type_attribute : The attribute type , any valid MISP attribute type is accepted .
: param category : The attribute category , any valid MISP attribute category is accepted .
: param org : Search by the creator organisation by supplying the organisation identifier .
: param tags : Tags to search or to exclude . You can pass a list , or the output of ` build_complex_query `
: param quick_filter : The string passed to this field will ignore all of the other arguments . MISP will return an xml / json ( depending on the header sent ) of all events that have a sub - string match on value in the event info , event orgc , or any of the attribute value1 / value2 fields , or in the attribute comment .
: param date_from : Events with the date set to a date after the one specified . This filter will use the date of the event .
: param date_to : Events with the date set to a date before the one specified . This filter will use the date of the event .
: param eventid : The events that should be included / excluded from the search
: param with_attachments : If set , encodes the attachments / zipped malware samples as base64 in the data field within each attribute
: param metadata : Only the metadata ( event , tags , relations ) is returned , attributes and proposals are omitted .
: param uuid : Restrict the results by uuid .
: param publish_timestamp : Restrict the results by the last publish timestamp ( newer than ) .
: param timestamp : Restrict the results by the timestamp ( last edit ) . Any event with a timestamp newer than the given timestamp will be returned . In case you are dealing with / attributes as scope , the attribute ' s timestamp will be used for the lookup.
: param published : Set whether published or unpublished events should be returned . Do not set the parameter if you want both .
: param enforce_warninglist : Remove any attributes from the result that would cause a hit on a warninglist entry .
: param to_ids : By default all attributes are returned that match the other filter parameters , irregardless of their to_ids setting . To restrict the returned data set to to_ids only attributes set this parameter to 1. 0 for the ones with to_ids set to False .
2020-06-19 11:32:02 +02:00
: param deleted : If this parameter is set to 1 , it will only return soft - deleted attributes . [ " 0 " , " 1 " ] will return the active ones as well as the soft - deleted ones .
2019-12-18 14:45:14 +01:00
: param include_event_uuid : Instead of just including the event ID , also include the event UUID in each of the attributes .
: param include_event_tags : Include the event level tags in each of the attributes .
: param event_timestamp : Only return attributes from events that have received a modification after the given timestamp .
: param sg_reference_only : If this flag is set , sharing group objects will not be included , instead only the sharing group ID is set .
: param eventinfo : Filter on the event ' s info field.
: param searchall : Search for a full or a substring ( delimited by % for substrings ) in the event info , event tags , attribute tags , attribute values or attribute comment fields .
: param requested_attributes : [ CSV only ] Select the fields that you wish to include in the CSV export . By setting event level fields additionally , includeContext is not required to get event metadata .
2020-03-02 17:33:38 +01:00
: param include_context : [ Attribute only ] Include the event data with each attribute . [ CSV output ] Add event level metadata in every line of the CSV .
2019-12-18 14:45:14 +01:00
: param headerless : [ CSV Only ] The CSV created when this setting is set to true will not contain the header row .
: param include_sightings : [ JSON Only - Attribute ] Include the sightings of the matching attributes .
2020-01-30 14:05:02 +01:00
: param include_decay_score : Include the decay score at attribute level .
2019-12-18 14:45:14 +01:00
: param include_correlations : [ JSON Only - attribute ] Include the correlations of the matching attributes .
2020-05-07 13:59:45 +02:00
: param object_name : [ objects controller only ] Search for objects with that name
2019-12-18 14:45:14 +01:00
: param pythonify : Returns a list of PyMISP Objects instead of the plain json output . Warning : it might use a lot of RAM
Deprecated :
: param quickFilter : synponym for quick_filter
: param withAttachments : synonym for with_attachments
: param last : synonym for publish_timestamp
: param enforceWarninglist : synonym for enforce_warninglist
: param includeEventUuid : synonym for include_event_uuid
: param includeEventTags : synonym for include_event_tags
: param includeContext : synonym for include_context
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
'''
2018-01-25 16:02:53 +01:00
2019-12-18 14:45:14 +01:00
return_formats = [ ' openioc ' , ' json ' , ' xml ' , ' suricata ' , ' snort ' , ' text ' , ' rpz ' , ' csv ' , ' cache ' , ' stix ' , ' stix2 ' , ' yara ' , ' yara-json ' , ' attack ' , ' attack-sightings ' ]
2018-01-25 16:02:53 +01:00
2020-05-07 13:59:45 +02:00
if controller not in [ ' events ' , ' attributes ' , ' objects ' ] :
raise ValueError ( ' controller has to be in {} ' . format ( ' , ' . join ( [ ' events ' , ' attributes ' , ' objects ' ] ) ) )
2019-12-18 14:45:14 +01:00
# Deprecated stuff / synonyms
if quickFilter is not None :
quick_filter = quickFilter
if withAttachments is not None :
with_attachments = withAttachments
if last is not None :
publish_timestamp = last
if enforceWarninglist is not None :
enforce_warninglist = enforceWarninglist
if includeEventUuid is not None :
include_event_uuid = includeEventUuid
if includeEventTags is not None :
include_event_tags = includeEventTags
if includeContext is not None :
include_context = includeContext
2020-01-30 14:05:02 +01:00
if includeDecayScore is not None :
include_decay_score = includeDecayScore
2019-12-18 14:45:14 +01:00
if includeCorrelations is not None :
include_correlations = includeCorrelations
if includeSightings is not None :
include_sightings = includeSightings
# Add all the parameters in kwargs are aimed at modules, or other 3rd party components, and cannot be sanitized.
# They are passed as-is.
query = kwargs
if return_format not in return_formats :
raise ValueError ( ' return_format has to be in {} ' . format ( ' , ' . join ( return_formats ) ) )
query [ ' returnFormat ' ] = return_format
query [ ' page ' ] = page
query [ ' limit ' ] = limit
query [ ' value ' ] = value
query [ ' type ' ] = type_attribute
query [ ' category ' ] = category
query [ ' org ' ] = org
query [ ' tags ' ] = tags
query [ ' quickFilter ' ] = quick_filter
query [ ' from ' ] = self . _make_timestamp ( date_from )
query [ ' to ' ] = self . _make_timestamp ( date_to )
query [ ' eventid ' ] = eventid
query [ ' withAttachments ' ] = self . _make_misp_bool ( with_attachments )
query [ ' metadata ' ] = self . _make_misp_bool ( metadata )
query [ ' uuid ' ] = uuid
if publish_timestamp is not None :
if isinstance ( publish_timestamp , ( list , tuple ) ) :
query [ ' publish_timestamp ' ] = ( self . _make_timestamp ( publish_timestamp [ 0 ] ) , self . _make_timestamp ( publish_timestamp [ 1 ] ) )
else :
query [ ' publish_timestamp ' ] = self . _make_timestamp ( publish_timestamp )
if timestamp is not None :
if isinstance ( timestamp , ( list , tuple ) ) :
query [ ' timestamp ' ] = ( self . _make_timestamp ( timestamp [ 0 ] ) , self . _make_timestamp ( timestamp [ 1 ] ) )
else :
query [ ' timestamp ' ] = self . _make_timestamp ( timestamp )
query [ ' published ' ] = published
query [ ' enforceWarninglist ' ] = self . _make_misp_bool ( enforce_warninglist )
if to_ids is not None :
2020-01-23 10:27:40 +01:00
if to_ids not in [ 0 , 1 , ' 0 ' , ' 1 ' ] :
raise ValueError ( ' to_ids has to be in 0 or 1 ' )
2019-12-18 14:45:14 +01:00
query [ ' to_ids ' ] = to_ids
query [ ' deleted ' ] = deleted
query [ ' includeEventUuid ' ] = self . _make_misp_bool ( include_event_uuid )
query [ ' includeEventTags ' ] = self . _make_misp_bool ( include_event_tags )
if event_timestamp is not None :
if isinstance ( event_timestamp , ( list , tuple ) ) :
query [ ' event_timestamp ' ] = ( self . _make_timestamp ( event_timestamp [ 0 ] ) , self . _make_timestamp ( event_timestamp [ 1 ] ) )
else :
query [ ' event_timestamp ' ] = self . _make_timestamp ( event_timestamp )
query [ ' sgReferenceOnly ' ] = self . _make_misp_bool ( sg_reference_only )
query [ ' eventinfo ' ] = eventinfo
query [ ' searchall ' ] = searchall
query [ ' requested_attributes ' ] = requested_attributes
query [ ' includeContext ' ] = self . _make_misp_bool ( include_context )
query [ ' headerless ' ] = self . _make_misp_bool ( headerless )
query [ ' includeSightings ' ] = self . _make_misp_bool ( include_sightings )
2020-01-30 14:05:02 +01:00
query [ ' includeDecayScore ' ] = self . _make_misp_bool ( include_decay_score )
2019-12-18 14:45:14 +01:00
query [ ' includeCorrelations ' ] = self . _make_misp_bool ( include_correlations )
2020-05-07 13:59:45 +02:00
query [ ' object_name ' ] = object_name
2019-12-18 14:45:14 +01:00
url = urljoin ( self . root_url , f ' { controller } /restSearch ' )
response = self . _prepare_request ( ' POST ' , url , data = query )
2018-09-25 16:32:32 +02:00
2020-01-23 10:27:40 +01:00
if return_format == ' csv ' :
normalized_response_text = self . _check_response ( response )
if ( self . global_pythonify or pythonify ) and not headerless :
return self . _csv_to_dict ( normalized_response_text ) # type: ignore
else :
return normalized_response_text
normalized_response = self . _check_json_response ( response )
2018-10-05 17:45:12 +02:00
2019-12-18 14:45:14 +01:00
if ' errors ' in normalized_response :
return normalized_response
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
if return_format == ' json ' and self . global_pythonify or pythonify :
# The response is in json, we can convert it to a list of pythonic MISP objects
2020-05-07 12:17:31 +02:00
to_return : List [ Union [ MISPEvent , MISPAttribute , MISPObject ] ] = [ ]
2019-12-18 14:45:14 +01:00
if controller == ' events ' :
for e in normalized_response :
me = MISPEvent ( )
me . load ( e )
to_return . append ( me )
elif controller == ' attributes ' :
# FIXME: obvs, this is hurting my soul. We need something generic.
2020-01-23 10:27:40 +01:00
for a in normalized_response [ ' Attribute ' ] :
2019-12-18 14:45:14 +01:00
ma = MISPAttribute ( )
ma . from_dict ( * * a )
if ' Event ' in ma :
me = MISPEvent ( )
me . from_dict ( * * ma . Event )
ma . Event = me
if ' RelatedAttribute ' in ma :
related_attributes = [ ]
for ra in ma . RelatedAttribute :
r_attribute = MISPAttribute ( )
r_attribute . from_dict ( * * ra )
if ' Event ' in r_attribute :
me = MISPEvent ( )
me . from_dict ( * * r_attribute . Event )
r_attribute . Event = me
related_attributes . append ( r_attribute )
ma . RelatedAttribute = related_attributes
if ' Sighting ' in ma :
sightings = [ ]
for sighting in ma . Sighting :
s = MISPSighting ( )
s . from_dict ( * * sighting )
sightings . append ( s )
ma . Sighting = sightings
to_return . append ( ma )
elif controller == ' objects ' :
2020-05-07 12:17:31 +02:00
for o in normalized_response :
mo = MISPObject ( o [ ' Object ' ] [ ' name ' ] )
mo . from_dict ( * * o )
to_return . append ( mo )
2019-12-18 14:45:14 +01:00
return to_return
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
return normalized_response
def search_index ( self , published : Optional [ bool ] = None , eventid : Optional [ SearchType ] = None ,
tags : Optional [ SearchParameterTypes ] = None ,
2020-01-23 10:27:40 +01:00
date_from : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
date_to : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
2019-12-18 14:45:14 +01:00
eventinfo : Optional [ str ] = None ,
threatlevel : Optional [ List [ SearchType ] ] = None ,
distribution : Optional [ List [ SearchType ] ] = None ,
analysis : Optional [ List [ SearchType ] ] = None ,
org : Optional [ SearchParameterTypes ] = None ,
2020-01-23 10:27:40 +01:00
timestamp : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2020-05-07 12:17:31 +02:00
pythonify : Optional [ bool ] = None ) - > Union [ Dict , List [ MISPEvent ] ] :
2019-12-18 14:45:14 +01:00
""" Search only at the index level. Using ! in front of a value means NOT (default is OR)
: param published : Set whether published or unpublished events should be returned . Do not set the parameter if you want both .
: param eventid : The events that should be included / excluded from the search
: param tags : Tags to search or to exclude . You can pass a list , or the output of ` build_complex_query `
: param date_from : Events with the date set to a date after the one specified . This filter will use the date of the event .
: param date_to : Events with the date set to a date before the one specified . This filter will use the date of the event .
: param eventinfo : Filter on the event ' s info field.
: param threatlevel : Threat level ( s ) ( 1 , 2 , 3 , 4 ) | list
: param distribution : Distribution level ( s ) ( 0 , 1 , 2 , 3 ) | list
: param analysis : Analysis level ( s ) ( 0 , 1 , 2 ) | list
: param org : Search by the creator organisation by supplying the organisation identifier .
: param timestamp : Restrict the results by the timestamp ( last edit ) . Any event with a timestamp newer than the given timestamp will be returned . In case you are dealing with / attributes as scope , the attribute ' s timestamp will be used for the lookup.
: param pythonify : Returns a list of PyMISP Objects instead or the plain json output . Warning : it might use a lot of RAM
"""
query = locals ( )
query . pop ( ' self ' )
query . pop ( ' pythonify ' )
if query . get ( ' date_from ' ) :
query [ ' datefrom ' ] = self . _make_timestamp ( query . pop ( ' date_from ' ) )
if query . get ( ' date_to ' ) :
query [ ' dateuntil ' ] = self . _make_timestamp ( query . pop ( ' date_to ' ) )
if query . get ( ' timestamp ' ) is not None :
timestamp = query . pop ( ' timestamp ' )
if isinstance ( timestamp , ( list , tuple ) ) :
query [ ' timestamp ' ] = ( self . _make_timestamp ( timestamp [ 0 ] ) , self . _make_timestamp ( timestamp [ 1 ] ) )
else :
query [ ' timestamp ' ] = self . _make_timestamp ( timestamp )
2019-02-01 14:43:01 +01:00
2019-12-18 14:45:14 +01:00
url = urljoin ( self . root_url , ' events/index ' )
response = self . _prepare_request ( ' POST ' , url , data = query )
2020-01-23 10:27:40 +01:00
normalized_response = self . _check_json_response ( response )
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) :
return normalized_response
to_return = [ ]
for e_meta in normalized_response :
me = MISPEvent ( )
me . from_dict ( * * e_meta )
to_return . append ( me )
return to_return
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
def search_sightings ( self , context : Optional [ str ] = None ,
context_id : Optional [ SearchType ] = None ,
type_sighting : Optional [ str ] = None ,
2020-01-23 10:27:40 +01:00
date_from : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
date_to : Optional [ Union [ datetime , date , int , str , float , None ] ] = None ,
publish_timestamp : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2020-01-23 10:27:40 +01:00
last : Optional [ Union [ Union [ datetime , date , int , str , float , None ] ,
Tuple [ Union [ datetime , date , int , str , float , None ] ,
Union [ datetime , date , int , str , float , None ] ]
2020-01-30 11:07:49 +01:00
] ] = None ,
2019-12-18 14:45:14 +01:00
org : Optional [ SearchType ] = None ,
source : Optional [ str ] = None ,
include_attribute : Optional [ bool ] = None ,
include_event_meta : Optional [ bool ] = None ,
pythonify : Optional [ bool ] = False
2020-05-07 12:17:31 +02:00
) - > Union [ Dict , List [ Dict [ str , Union [ MISPEvent , MISPAttribute , MISPSighting ] ] ] ] :
2019-12-18 14:45:14 +01:00
''' Search sightings
: param context : The context of the search . Can be either " attribute " , " event " , or nothing ( will then match on events and attributes ) .
: param context_id : Only relevant if context is either " attribute " or " event " . Then it is the relevant ID .
: param type_sighting : Type of sighting
: param date_from : Events with the date set to a date after the one specified . This filter will use the date of the event .
: param date_to : Events with the date set to a date before the one specified . This filter will use the date of the event .
: param publish_timestamp : Restrict the results by the last publish timestamp ( newer than ) .
: param org : Search by the creator organisation by supplying the organisation identifier .
: param source : Source of the sighting
: param include_attribute : Include the attribute .
: param include_event_meta : Include the meta information of the event .
Deprecated :
: param last : synonym for publish_timestamp
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
: Example :
2018-09-25 17:15:38 +02:00
2019-12-18 14:45:14 +01:00
>> > misp . search_sightings ( publish_timestamp = ' 30d ' ) # search sightings for the last 30 days on the instance
[ . . . ]
>> > misp . search_sightings ( context = ' attribute ' , context_id = 6 , include_attribute = True ) # return list of sighting for attribute 6 along with the attribute itself
[ . . . ]
>> > misp . search_sightings ( context = ' event ' , context_id = 17 , include_event_meta = True , org = 2 ) # return list of sighting for event 17 filtered with org id 2
'''
2020-01-23 10:27:40 +01:00
query : Dict [ str , Any ] = { ' returnFormat ' : ' json ' }
2019-12-18 14:45:14 +01:00
if context is not None :
if context not in [ ' attribute ' , ' event ' ] :
raise ValueError ( ' context has to be in {} ' . format ( ' , ' . join ( [ ' attribute ' , ' event ' ] ) ) )
url_path = f ' sightings/restSearch/ { context } '
else :
url_path = ' sightings/restSearch '
if isinstance ( context_id , ( MISPEvent , MISPAttribute ) ) :
2020-05-07 12:17:31 +02:00
context_id = get_uuid_or_id_from_abstract_misp ( context_id )
2019-12-18 14:45:14 +01:00
query [ ' id ' ] = context_id
query [ ' type ' ] = type_sighting
query [ ' from ' ] = date_from
query [ ' to ' ] = date_to
query [ ' last ' ] = publish_timestamp
query [ ' org_id ' ] = org
query [ ' source ' ] = source
query [ ' includeAttribute ' ] = include_attribute
query [ ' includeEvent ' ] = include_event_meta
url = urljoin ( self . root_url , url_path )
response = self . _prepare_request ( ' POST ' , url , data = query )
2020-01-23 10:27:40 +01:00
normalized_response = self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in normalized_response :
return normalized_response
if self . global_pythonify or pythonify :
to_return = [ ]
for s in normalized_response :
2020-01-23 10:27:40 +01:00
entries : Dict [ str , Union [ MISPEvent , MISPAttribute , MISPSighting ] ] = { }
2019-12-18 14:45:14 +01:00
s_data = s [ ' Sighting ' ]
if include_event_meta :
e = s_data . pop ( ' Event ' )
me = MISPEvent ( )
me . from_dict ( * * e )
entries [ ' event ' ] = me
if include_attribute :
a = s_data . pop ( ' Attribute ' )
ma = MISPAttribute ( )
ma . from_dict ( * * a )
entries [ ' attribute ' ] = ma
ms = MISPSighting ( )
ms . from_dict ( * * s_data )
entries [ ' sighting ' ] = ms
to_return . append ( entries )
return to_return
return normalized_response
def search_logs ( self , limit : Optional [ int ] = None , page : Optional [ int ] = None ,
log_id : Optional [ int ] = None , title : Optional [ str ] = None ,
2020-01-23 10:27:40 +01:00
created : Optional [ Union [ datetime , date , int , str , float , None ] ] = None , model : Optional [ str ] = None ,
2019-12-18 14:45:14 +01:00
action : Optional [ str ] = None , user_id : Optional [ int ] = None ,
change : Optional [ str ] = None , email : Optional [ str ] = None ,
org : Optional [ str ] = None , description : Optional [ str ] = None ,
2020-05-07 12:17:31 +02:00
ip : Optional [ str ] = None , pythonify : Optional [ bool ] = False ) - > Union [ Dict , List [ MISPLog ] ] :
2019-12-18 14:45:14 +01:00
''' Search in logs
Note : to run substring queries simply append / prepend / encapsulate the search term with %
: param limit : Limit the number of results returned , depending on the scope ( for example 10 attributes or 10 full events ) .
: param page : If a limit is set , sets the page to be returned . page 3 , limit 100 will return records 201 - > 300 ) .
: param log_id : Log ID
: param title : Log Title
: param created : Creation timestamp
: param model : Model name that generated the log entry
: param action : The thing that was done
: param user_id : ID of the user doing the action
: param change : Change that occured
: param email : Email of the user
: param org : Organisation of the User doing the action
: param description : Description of the action
: param ip : Origination IP of the User doing the action
: param pythonify : Returns a list of PyMISP Objects instead or the plain json output . Warning : it might use a lot of RAM
'''
query = locals ( )
query . pop ( ' self ' )
query . pop ( ' pythonify ' )
if log_id is not None :
query [ ' id ' ] = query . pop ( ' log_id ' )
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , ' admin/logs/index ' , data = query )
2020-01-23 10:27:40 +01:00
normalized_response = self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in normalized_response :
return normalized_response
2018-10-04 19:03:24 +02:00
2019-12-18 14:45:14 +01:00
to_return = [ ]
2020-05-12 11:34:38 +02:00
for log in normalized_response :
2019-12-18 14:45:14 +01:00
ml = MISPLog ( )
2020-05-12 11:34:38 +02:00
ml . from_dict ( * * log )
2019-12-18 14:45:14 +01:00
to_return . append ( ml )
return to_return
2018-01-29 10:30:37 +01:00
2020-05-07 12:17:31 +02:00
def search_feeds ( self , value : Optional [ SearchParameterTypes ] = None , pythonify : Optional [ bool ] = False ) - > Union [ Dict , List [ MISPFeed ] ] :
2019-12-18 14:45:14 +01:00
''' Search in the feeds cached on the servers '''
response = self . _prepare_request ( ' POST ' , ' /feeds/searchCaches ' , data = { ' value ' : value } )
2020-01-23 10:27:40 +01:00
normalized_response = self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in normalized_response :
return normalized_response
to_return = [ ]
for feed in normalized_response :
f = MISPFeed ( )
f . from_dict ( * * feed )
to_return . append ( f )
return to_return
2018-01-29 10:30:37 +01:00
2019-12-18 14:45:14 +01:00
# ## END Search methods ###
2018-01-29 10:30:37 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Communities ###
2018-09-28 18:14:27 +02:00
2020-05-07 12:17:31 +02:00
def communities ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPCommunity ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the communities. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' communities ' )
communities = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in communities :
return communities
to_return = [ ]
for community in communities :
c = MISPCommunity ( )
c . from_dict ( * * community )
to_return . append ( c )
return to_return
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def get_community ( self , community : Union [ MISPCommunity , int , str , UUID ] , pythonify : bool = False ) - > Union [ Dict , MISPCommunity ] :
2019-12-18 14:45:14 +01:00
''' Get an community from a MISP instance '''
2020-05-07 12:17:31 +02:00
community_id = get_uuid_or_id_from_abstract_misp ( community )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , f ' communities/view/ { community_id } ' )
community_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in community_j :
return community_j
2019-12-18 14:45:14 +01:00
c = MISPCommunity ( )
2020-01-23 10:27:40 +01:00
c . from_dict ( * * community_j )
2019-12-18 14:45:14 +01:00
return c
def request_community_access ( self , community : Union [ MISPCommunity , int , str , UUID ] ,
2020-05-07 12:17:31 +02:00
requestor_email_address : Optional [ str ] = None ,
requestor_gpg_key : Optional [ str ] = None ,
requestor_organisation_name : Optional [ str ] = None ,
requestor_organisation_uuid : Optional [ str ] = None ,
requestor_organisation_description : Optional [ str ] = None ,
message : Optional [ str ] = None , sync : bool = False ,
2019-12-18 14:45:14 +01:00
anonymise_requestor_server : bool = False ,
2020-05-07 12:17:31 +02:00
mock : bool = False ) - > Dict :
community_id = get_uuid_or_id_from_abstract_misp ( community )
2019-12-18 14:45:14 +01:00
to_post = { ' org_name ' : requestor_organisation_name ,
' org_uuid ' : requestor_organisation_uuid ,
' org_description ' : requestor_organisation_description ,
' email ' : requestor_email_address , ' gpgkey ' : requestor_gpg_key ,
' message ' : message , ' anonymise ' : anonymise_requestor_server , ' sync ' : sync ,
' mock ' : mock }
r = self . _prepare_request ( ' POST ' , f ' communities/requestAccess/ { community_id } ' , data = to_post )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
# ## END Communities ###
# ## BEGIN Event Delegation ###
2020-05-07 12:17:31 +02:00
def event_delegations ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPEventDelegation ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the event delegations. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' event_delegations ' )
delegations = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in delegations :
return delegations
to_return = [ ]
for delegation in delegations :
d = MISPEventDelegation ( )
d . from_dict ( * * delegation )
to_return . append ( d )
return to_return
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def accept_event_delegation ( self , delegation : Union [ MISPEventDelegation , int , str ] , pythonify : bool = False ) - > Dict :
delegation_id = get_uuid_or_id_from_abstract_misp ( delegation )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' event_delegations/acceptDelegation/ { delegation_id } ' )
return self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def discard_event_delegation ( self , delegation : Union [ MISPEventDelegation , int , str ] , pythonify : bool = False ) - > Dict :
delegation_id = get_uuid_or_id_from_abstract_misp ( delegation )
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' event_delegations/deleteDelegation/ { delegation_id } ' )
return self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
2020-01-03 15:42:15 +01:00
def delegate_event ( self , event : Optional [ Union [ MISPEvent , int , str , UUID ] ] = None ,
organisation : Optional [ Union [ MISPOrganisation , int , str , UUID ] ] = None ,
event_delegation : Optional [ MISPEventDelegation ] = None ,
2020-05-07 12:17:31 +02:00
distribution : int = - 1 , message : str = ' ' , pythonify : bool = False ) - > Union [ Dict , MISPEventDelegation ] :
2019-12-18 14:45:14 +01:00
''' Note: distribution == -1 means recipient decides '''
if event and organisation :
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
organisation_id = get_uuid_or_id_from_abstract_misp ( organisation )
2019-12-18 14:45:14 +01:00
data = { ' event_id ' : event_id , ' org_id ' : organisation_id , ' distribution ' : distribution , ' message ' : message }
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' event_delegations/delegateEvent/ { event_id } ' , data = data )
2019-12-18 14:45:14 +01:00
elif event_delegation :
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' event_delegations/delegateEvent/ { event_id } ' , data = event_delegation )
2019-12-18 14:45:14 +01:00
else :
raise PyMISPError ( ' Either event and organisation OR event_delegation are required. ' )
2020-01-23 10:27:40 +01:00
delegation_j = self . _check_json_response ( r )
if not ( self . global_pythonify or pythonify ) or ' errors ' in delegation_j :
return delegation_j
2019-12-18 14:45:14 +01:00
d = MISPEventDelegation ( )
2020-01-23 10:27:40 +01:00
d . from_dict ( * * delegation_j )
2019-12-18 14:45:14 +01:00
return d
2017-03-09 16:32:51 +01:00
2019-12-18 14:45:14 +01:00
# ## END Event Delegation ###
2017-03-09 16:32:51 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Others ###
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def push_event_to_ZMQ ( self , event : Union [ MISPEvent , int , str , UUID ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Force push an event on ZMQ """
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , f ' events/pushEventToZMQ/ { event_id } .json ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-03-09 16:32:51 +01:00
2020-05-07 12:17:31 +02:00
def direct_call ( self , url : str , data : Optional [ Dict ] = None , params : Mapping = { } , kw_params : Mapping = { } ) - > Any :
2019-12-18 14:45:14 +01:00
''' Very lightweight call that posts a data blob (python dictionary or json string) on the URL '''
if data is None :
response = self . _prepare_request ( ' GET ' , url , params = params , kw_params = kw_params )
else :
response = self . _prepare_request ( ' POST ' , url , data = data , params = params , kw_params = kw_params )
return self . _check_response ( response , lenient_response_type = True )
2017-03-09 16:32:51 +01:00
2019-12-18 14:45:14 +01:00
def freetext ( self , event : Union [ MISPEvent , int , str , UUID ] , string : str , adhereToWarninglists : Union [ bool , str ] = False ,
2020-05-07 12:17:31 +02:00
distribution : Optional [ int ] = None , returnMetaAttributes : bool = False , pythonify : bool = False , * * kwargs ) - > Union [ Dict , List [ MISPAttribute ] ] :
2019-12-18 14:45:14 +01:00
""" Pass a text to the freetext importer """
2020-05-07 12:17:31 +02:00
event_id = get_uuid_or_id_from_abstract_misp ( event )
2020-01-23 10:27:40 +01:00
query : Dict [ str , Any ] = { " value " : string }
2019-12-18 14:45:14 +01:00
wl_params = [ False , True , ' soft ' ]
if adhereToWarninglists in wl_params :
query [ ' adhereToWarninglists ' ] = adhereToWarninglists
else :
2020-01-23 10:27:40 +01:00
raise PyMISPError ( ' Invalid parameter, adhereToWarninglists Can only be False, True, or soft ' )
2019-12-18 14:45:14 +01:00
if distribution is not None :
query [ ' distribution ' ] = distribution
if returnMetaAttributes :
query [ ' returnMetaAttributes ' ] = returnMetaAttributes
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' POST ' , f ' events/freeTextImport/ { event_id } ' , data = query , * * kwargs )
attributes = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if returnMetaAttributes or not ( self . global_pythonify or pythonify ) or ' errors ' in attributes :
return attributes
to_return = [ ]
for attribute in attributes :
a = MISPAttribute ( )
a . from_dict ( * * attribute )
to_return . append ( a )
return to_return
2017-03-09 16:32:51 +01:00
2020-01-23 10:27:40 +01:00
def upload_stix ( self , path , version : str = ' 2 ' ) :
2019-12-18 14:45:14 +01:00
""" 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
2017-12-01 12:01:42 +01:00
"""
2019-12-18 14:45:14 +01:00
if isinstance ( path , ( str , Path ) ) :
with open ( path , ' rb ' ) as f :
to_post = f . read ( )
2017-12-01 12:01:42 +01:00
else :
2019-12-18 14:45:14 +01:00
to_post = path . read ( )
2018-04-25 16:44:00 +02:00
2019-12-18 14:45:14 +01:00
if isinstance ( to_post , bytes ) :
2020-01-23 10:27:40 +01:00
to_post = to_post . decode ( ) # type: ignore
2018-04-25 16:44:00 +02:00
2019-12-18 14:45:14 +01:00
if str ( version ) == ' 1 ' :
url = urljoin ( self . root_url , ' /events/upload_stix ' )
2020-01-23 10:27:40 +01:00
response = self . _prepare_request ( ' POST ' , url , data = to_post , output_type = ' xml ' ) # type: ignore
2019-12-18 14:45:14 +01:00
else :
url = urljoin ( self . root_url , ' /events/upload_stix/2 ' )
2020-01-23 10:27:40 +01:00
response = self . _prepare_request ( ' POST ' , url , data = to_post ) # type: ignore
2019-12-18 14:45:14 +01:00
return response
2018-04-25 16:44:00 +02:00
2019-12-18 14:45:14 +01:00
# ## END Others ###
2018-04-25 16:44:00 +02:00
2019-12-18 14:45:14 +01:00
# ## BEGIN Statistics ###
2018-04-25 11:06:03 +02:00
2020-05-07 12:17:31 +02:00
def attributes_statistics ( self , context : str = ' type ' , percentage : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Get attributes statistics from the MISP instance. """
# FIXME: https://github.com/MISP/MISP/issues/4874
if context not in [ ' type ' , ' category ' ] :
raise PyMISPError ( ' context can only be " type " or " category " ' )
if percentage :
path = f ' attributes/attributeStatistics/ { context } /true '
else :
path = f ' attributes/attributeStatistics/ { context } '
response = self . _prepare_request ( ' GET ' , path )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-07-03 22:56:44 +02:00
2020-05-07 12:17:31 +02:00
def tags_statistics ( self , percentage : bool = False , name_sort : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Get tags statistics from the MISP instance """
# FIXME: https://github.com/MISP/MISP/issues/4874
# NOTE: https://github.com/MISP/MISP/issues/4879
if percentage :
2020-01-23 10:27:40 +01:00
p = ' true '
2019-12-18 14:45:14 +01:00
else :
2020-01-23 10:27:40 +01:00
p = ' false '
2019-12-18 14:45:14 +01:00
if name_sort :
2020-01-23 10:27:40 +01:00
ns = ' true '
2019-12-18 14:45:14 +01:00
else :
2020-01-23 10:27:40 +01:00
ns = ' false '
response = self . _prepare_request ( ' GET ' , f ' tags/tagStatistics/ { p } / { ns } ' )
return self . _check_json_response ( response )
2017-07-03 22:56:44 +02:00
2020-05-07 12:17:31 +02:00
def users_statistics ( self , context : str = ' data ' ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Get users statistics from the MISP instance """
availables_contexts = [ ' data ' , ' orgs ' , ' users ' , ' tags ' , ' attributehistogram ' , ' sightings ' , ' galaxyMatrix ' ]
if context not in availables_contexts :
raise PyMISPError ( " context can only be { ' , ' .join(availables_contexts)} " )
response = self . _prepare_request ( ' GET ' , f ' users/statistics/ { context } ' )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2017-07-03 22:56:44 +02:00
2019-12-18 14:45:14 +01:00
# ## END Statistics ###
2018-02-13 11:25:05 +01:00
2019-12-18 14:45:14 +01:00
# ## BEGIN User Settings ###
2018-02-13 11:25:05 +01:00
2020-05-07 12:17:31 +02:00
def user_settings ( self , pythonify : bool = False ) - > Union [ Dict , List [ MISPUserSetting ] ] :
2019-12-18 14:45:14 +01:00
""" Get all the user settings. """
2020-01-23 10:27:40 +01:00
r = self . _prepare_request ( ' GET ' , ' user_settings ' )
user_settings = self . _check_json_response ( r )
2019-12-18 14:45:14 +01:00
if not ( self . global_pythonify or pythonify ) or ' errors ' in user_settings :
return user_settings
to_return = [ ]
for user_setting in user_settings :
u = MISPUserSetting ( )
u . from_dict ( * * user_setting )
to_return . append ( u )
return to_return
2018-02-13 11:25:05 +01:00
2020-01-03 15:42:15 +01:00
def get_user_setting ( self , user_setting : str , user : Optional [ Union [ MISPUser , int , str , UUID ] ] = None ,
2020-05-07 12:17:31 +02:00
pythonify : bool = False ) - > Union [ Dict , MISPUserSetting ] :
2019-12-18 14:45:14 +01:00
''' Get an user setting '''
2020-01-23 10:27:40 +01:00
query : Dict [ str , Any ] = { ' setting ' : user_setting }
2019-12-18 14:45:14 +01:00
if user :
2020-05-07 12:17:31 +02:00
query [ ' user_id ' ] = get_uuid_or_id_from_abstract_misp ( user )
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' POST ' , ' user_settings/getSetting ' )
2020-01-23 10:27:40 +01:00
user_setting_j = self . _check_json_response ( response )
if not ( self . global_pythonify or pythonify ) or ' errors ' in user_setting_j :
return user_setting_j
2019-12-18 14:45:14 +01:00
u = MISPUserSetting ( )
2020-01-23 10:27:40 +01:00
u . from_dict ( * * user_setting_j )
2019-12-18 14:45:14 +01:00
return u
2020-01-03 15:42:15 +01:00
def set_user_setting ( self , user_setting : str , value : Union [ str , dict ] , user : Optional [ Union [ MISPUser , int , str , UUID ] ] = None ,
2020-05-07 12:17:31 +02:00
pythonify : bool = False ) - > Union [ Dict , MISPUserSetting ] :
2019-12-18 14:45:14 +01:00
''' Get an user setting '''
2020-01-23 10:27:40 +01:00
query : Dict [ str , Any ] = { ' setting ' : user_setting }
2019-12-18 14:45:14 +01:00
if isinstance ( value , dict ) :
value = json . dumps ( value )
query [ ' value ' ] = value
if user :
2020-05-07 12:17:31 +02:00
query [ ' user_id ' ] = get_uuid_or_id_from_abstract_misp ( user )
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' POST ' , ' user_settings/setSetting ' , data = query )
2020-01-23 10:27:40 +01:00
user_setting_j = self . _check_json_response ( response )
if not ( self . global_pythonify or pythonify ) or ' errors ' in user_setting_j :
return user_setting_j
2019-12-18 14:45:14 +01:00
u = MISPUserSetting ( )
2020-01-23 10:27:40 +01:00
u . from_dict ( * * user_setting_j )
2019-12-18 14:45:14 +01:00
return u
2020-05-07 12:17:31 +02:00
def delete_user_setting ( self , user_setting : str , user : Optional [ Union [ MISPUser , int , str , UUID ] ] = None ) - > Dict :
2019-12-18 14:45:14 +01:00
''' Delete a user setting '''
2020-01-23 10:27:40 +01:00
query : Dict [ str , Any ] = { ' setting ' : user_setting }
2019-12-18 14:45:14 +01:00
if user :
2020-05-07 12:17:31 +02:00
query [ ' user_id ' ] = get_uuid_or_id_from_abstract_misp ( user )
2020-05-12 11:34:38 +02:00
response = self . _prepare_request ( ' POST ' , ' user_settings/delete ' , data = query )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
# ## END User Settings ###
# ## BEGIN Global helpers ###
2020-05-07 12:17:31 +02:00
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 ] :
2019-12-18 14:45:14 +01:00
""" Change the sharing group of an event, an attribute, or an object """
misp_entity . distribution = 4 # Needs to be 'Sharing group'
if ' SharingGroup ' in misp_entity : # Delete former SharingGroup information
del misp_entity . SharingGroup
misp_entity . sharing_group_id = sharing_group_id # Set new sharing group id
if isinstance ( misp_entity , MISPEvent ) :
return self . update_event ( misp_entity , pythonify = pythonify )
if isinstance ( misp_entity , MISPObject ) :
return self . update_object ( misp_entity , pythonify = pythonify )
if isinstance ( misp_entity , MISPAttribute ) :
return self . update_attribute ( misp_entity , pythonify = pythonify )
raise PyMISPError ( ' The misp_entity must be MISPEvent, MISPObject or MISPAttribute ' )
2020-05-07 12:17:31 +02:00
def tag ( self , misp_entity : Union [ AbstractMISP , str , dict ] , tag : Union [ MISPTag , str ] , local : bool = False ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Tag an event or an attribute. misp_entity can be a MISPEvent, a MISP Attribute, or a UUID """
2020-01-23 10:27:40 +01:00
if isinstance ( misp_entity , AbstractMISP ) and ' uuid ' in misp_entity :
2019-12-18 14:45:14 +01:00
uuid = misp_entity . uuid
2020-01-30 11:07:49 +01:00
elif isinstance ( misp_entity , dict ) and ' uuid ' in misp_entity :
uuid = misp_entity [ ' uuid ' ]
2020-01-23 10:27:40 +01:00
elif isinstance ( misp_entity , str ) :
2019-12-18 14:45:14 +01:00
uuid = misp_entity
if isinstance ( tag , MISPTag ) :
tag = tag . name
to_post = { ' uuid ' : uuid , ' tag ' : tag , ' local ' : local }
response = self . _prepare_request ( ' POST ' , ' tags/attachTagToObject ' , data = to_post )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
2020-05-07 12:17:31 +02:00
def untag ( self , misp_entity : Union [ AbstractMISP , str , dict ] , tag : Union [ MISPTag , str ] ) - > Dict :
2019-12-18 14:45:14 +01:00
""" Untag an event or an attribute. misp_entity can be a UUID """
2020-01-23 10:27:40 +01:00
if isinstance ( misp_entity , AbstractMISP ) and ' uuid ' in misp_entity :
2019-12-18 14:45:14 +01:00
uuid = misp_entity . uuid
2020-01-30 11:07:49 +01:00
elif isinstance ( misp_entity , dict ) and ' uuid ' in misp_entity :
uuid = misp_entity [ ' uuid ' ]
2020-01-23 10:27:40 +01:00
elif isinstance ( misp_entity , str ) :
2019-12-18 14:45:14 +01:00
uuid = misp_entity
if isinstance ( tag , MISPTag ) :
2020-01-23 10:27:40 +01:00
if ' name ' in tag :
tag_name = tag . name
else :
tag_name = tag
to_post = { ' uuid ' : uuid , ' tag ' : tag_name }
2019-12-18 14:45:14 +01:00
response = self . _prepare_request ( ' POST ' , ' tags/removeTagFromObject ' , data = to_post )
2020-01-23 10:27:40 +01:00
return self . _check_json_response ( response )
2019-12-18 14:45:14 +01:00
def build_complex_query ( self , or_parameters : Optional [ List [ SearchType ] ] = None ,
and_parameters : Optional [ List [ SearchType ] ] = None ,
2020-05-14 13:09:55 +02:00
not_parameters : Optional [ List [ SearchType ] ] = None ) - > Dict [ str , List [ SearchType ] ] :
2019-12-18 14:45:14 +01:00
''' Build a complex search query. MISP expects a dictionary with AND, OR and NOT keys. '''
to_return = { }
if and_parameters :
2020-05-14 13:09:55 +02:00
to_return [ ' AND ' ] = [ p for p in and_parameters if p ]
2019-12-18 14:45:14 +01:00
if not_parameters :
2020-05-14 13:09:55 +02:00
to_return [ ' NOT ' ] = [ p for p in not_parameters if p ]
2019-12-18 14:45:14 +01:00
if or_parameters :
2020-05-14 13:09:55 +02:00
to_return [ ' OR ' ] = [ p for p in or_parameters if p ]
2019-12-18 14:45:14 +01:00
return to_return
2018-02-13 11:25:05 +01:00
2019-12-18 14:45:14 +01:00
# ## END Global helpers ###
2018-02-13 11:25:05 +01:00
2019-12-18 14:45:14 +01:00
# ## Internal methods ###
2018-05-08 11:43:27 +02:00
2020-01-03 15:42:15 +01:00
def _old_misp ( self , minimal_version_required : tuple , removal_date : Union [ str , date , datetime ] , method : Optional [ str ] = None , message : Optional [ str ] = None ) - > bool :
2019-12-18 14:45:14 +01:00
if self . _misp_version > = minimal_version_required :
return False
if isinstance ( removal_date , ( datetime , date ) ) :
removal_date = removal_date . isoformat ( )
to_print = f ' The instance of MISP you are using is outdated. Unless you update your MISP instance, { method } will stop working after { removal_date } . '
if message :
to_print + = f ' { message } '
warnings . warn ( to_print , DeprecationWarning )
return True
2018-05-08 11:43:27 +02:00
2020-01-03 15:42:15 +01:00
def _make_misp_bool ( self , parameter : Optional [ Union [ bool , str ] ] = None ) - > int :
2019-12-18 14:45:14 +01:00
''' MISP wants 0 or 1 for bool, so we avoid True/False ' 0 ' , ' 1 ' '''
if parameter is None :
return 0
return 1 if int ( parameter ) else 0
2020-01-23 10:27:40 +01:00
def _make_timestamp ( self , value : Union [ datetime , date , int , str , float , None ] ) - > Union [ str , int , float , None ] :
2019-12-18 14:45:14 +01:00
''' Catch-all method to normalize anything that can be converted to a timestamp '''
2020-01-23 10:27:40 +01:00
if not value :
return None
2019-12-18 14:45:14 +01:00
if isinstance ( value , datetime ) :
return value . timestamp ( )
if isinstance ( value , date ) :
return datetime . combine ( value , datetime . max . time ( ) ) . timestamp ( )
if isinstance ( value , str ) :
if value . isdigit ( ) :
return value
try :
float ( value )
return value
except ValueError :
# The value can also be '1d', '10h', ...
return value
return value
2017-07-21 18:47:10 +02:00
2020-05-07 12:17:31 +02:00
def _check_json_response ( self , response : requests . Response ) - > Dict : # type: ignore
2020-01-23 10:27:40 +01:00
r = self . _check_response ( response , expect_json = True )
if isinstance ( r , ( dict , list ) ) :
return r
# Else: an exception was raised anyway
2020-05-07 12:17:31 +02:00
def _check_response ( self , response : requests . Response , lenient_response_type : bool = False , expect_json : bool = False ) - > Union [ Dict , str ] :
2019-12-18 14:45:14 +01:00
""" Check if the response from the server is not an unexpected error """
if response . status_code > = 500 :
logger . critical ( everything_broken . format ( response . request . headers , response . request . body , response . text ) )
raise MISPServerError ( f ' Error code 500: \n { response . text } ' )
2017-11-23 17:51:04 +01:00
2019-12-18 14:45:14 +01:00
if 400 < = response . status_code < 500 :
# The server returns a json message with the error details
try :
error_message = response . json ( )
except Exception :
raise MISPServerError ( f ' Error code { response . status_code } : \n { response . text } ' )
2017-08-23 15:36:13 +02:00
2019-12-18 14:45:14 +01:00
logger . error ( f ' Something went wrong ( { response . status_code } ): { error_message } ' )
return { ' errors ' : ( response . status_code , error_message ) }
2017-11-23 17:51:04 +01:00
2019-12-18 14:45:14 +01:00
# At this point, we had no error.
2017-07-24 17:16:40 +02:00
2019-12-18 14:45:14 +01:00
try :
2020-01-23 10:27:40 +01:00
response_json = response . json ( )
2019-12-18 14:45:14 +01:00
if logger . isEnabledFor ( logging . DEBUG ) :
2020-01-23 10:27:40 +01:00
logger . debug ( response_json )
if isinstance ( response_json , dict ) and response_json . get ( ' response ' ) is not None :
2019-12-18 14:45:14 +01:00
# Cleanup.
2020-01-23 10:27:40 +01:00
response_json = response_json [ ' response ' ]
return response_json
2019-12-18 14:45:14 +01:00
except Exception :
if logger . isEnabledFor ( logging . DEBUG ) :
logger . debug ( response . text )
if expect_json :
raise PyMISPUnexpectedResponse ( f ' Unexpected response from server: { response . text } ' )
2020-01-23 10:27:40 +01:00
if lenient_response_type and not response . headers [ ' content-type ' ] . startswith ( ' application/json ' ) :
2019-12-18 14:45:14 +01:00
return response . text
if not response . content :
# Empty response
logger . error ( ' Got an empty response. ' )
return { ' errors ' : ' The response is empty. ' }
return response . text
2019-03-11 10:15:39 +01:00
2019-12-18 14:45:14 +01:00
def __repr__ ( self ) :
return f ' < { self . __class__ . __name__ } (url= { self . root_url } ) '
2017-07-24 17:16:40 +02:00
2020-05-07 12:17:31 +02:00
def _prepare_request ( self , request_type : str , url : str , data : Union [ str , Iterator , Mapping , AbstractMISP ] = { } , params : Mapping = { } ,
kw_params : Mapping = { } , output_type : str = ' json ' ) - > requests . Response :
2019-12-18 14:45:14 +01:00
''' Prepare a request for python-requests '''
url = urljoin ( self . root_url , url )
2020-01-23 10:27:40 +01:00
if data == { } or isinstance ( data , str ) :
d = data
elif data :
2019-12-18 14:45:14 +01:00
if not isinstance ( data , str ) : # Else, we already have a text blob to send
if isinstance ( data , dict ) : # Else, we can directly json encode.
# Remove None values.
data = { k : v for k , v in data . items ( ) if v is not None }
2020-01-23 10:27:40 +01:00
d = json . dumps ( data , default = pymisp_json_default )
2018-09-25 17:15:38 +02:00
2019-12-18 14:45:14 +01:00
if logger . isEnabledFor ( logging . DEBUG ) :
logger . debug ( f ' { request_type } - { url } ' )
2020-01-23 10:27:40 +01:00
if d is not None :
logger . debug ( d )
2017-03-09 16:32:51 +01:00
2019-12-18 14:45:14 +01:00
if kw_params :
# CakePHP params in URL
to_append_url = ' / ' . join ( [ f ' { k } : { v } ' for k , v in kw_params . items ( ) ] )
url = f ' { url } / { to_append_url } '
2020-01-23 10:27:40 +01:00
req = requests . Request ( request_type , url , data = d , params = params )
2019-12-18 14:45:14 +01:00
with requests . Session ( ) as s :
user_agent = f ' PyMISP { __version__ } - Python { " . " . join ( str ( x ) for x in sys . version_info [ : 2 ] ) } '
if self . tool :
user_agent = f ' { user_agent } - { self . tool } '
req . auth = self . auth
prepped = s . prepare_request ( req )
prepped . headers . update (
{ ' Authorization ' : self . key ,
' Accept ' : f ' application/ { output_type } ' ,
' content-type ' : f ' application/ { output_type } ' ,
' User-Agent ' : user_agent } )
if logger . isEnabledFor ( logging . DEBUG ) :
logger . debug ( prepped . headers )
settings = s . merge_environment_settings ( req . url , proxies = self . proxies or { } , stream = None , verify = self . ssl , cert = self . cert )
2020-05-21 20:31:19 +02:00
return s . send ( prepped , timeout = self . timeout , * * settings )
2017-03-09 16:32:51 +01:00
2020-01-03 15:42:15 +01:00
def _csv_to_dict ( self , csv_content : str ) - > List [ dict ] :
2019-12-18 14:45:14 +01:00
''' Makes a list of dict out of a csv file (requires headers) '''
fieldnames , lines = csv_content . split ( ' \n ' , 1 )
2020-01-23 10:27:40 +01:00
fields = fieldnames . split ( ' , ' )
2019-12-18 14:45:14 +01:00
to_return = [ ]
for line in csv . reader ( lines . split ( ' \n ' ) ) :
if line :
2020-01-23 10:27:40 +01:00
to_return . append ( { fname : value for fname , value in zip ( fields , line ) } )
2019-12-18 14:45:14 +01:00
return to_return