@@ -48,13 +48,22 @@ def __init__(self, name, data, mime="text/plain"):
4848
4949
5050class OpenCTIApiClient :
51- """
52- Python API for OpenCTI
53- :param url: OpenCTI URL
54- :param token: The API key
51+ """Main API client for OpenCTI
52+
53+ :param url: OpenCTI API url
54+ :type url: str
55+ :param token: OpenCTI API token
56+ :type token: str
57+ :param log_level: log level for the client
58+ :type log_level: str, optional
59+ :param ssl_verify:
60+ :type ssl_verify: bool, optional
5561 """
5662
5763 def __init__ (self , url , token , log_level = "info" , ssl_verify = False ):
64+ """Constructor method
65+ """
66+
5867 # Check configuration
5968 self .ssl_verify = ssl_verify
6069 if url is None or len (token ) == 0 :
@@ -114,12 +123,34 @@ def __init__(self, url, token, log_level="info", ssl_verify=False):
114123 )
115124
116125 def get_token (self ):
126+ """Get the API token
127+
128+ :return: returns the configured API token
129+ :rtype: str
130+ """
131+
117132 return self .api_token
118133
119134 def set_token (self , token ):
135+ """set the request header with the specified token
136+
137+ :param token: OpenCTI API token
138+ :type token: str
139+ """
140+
120141 self .request_headers = {"Authorization" : "Bearer " + token }
121142
122143 def query (self , query , variables = {}):
144+ """submit a query to the OpenCTI GraphQL API
145+
146+ :param query: GraphQL query string
147+ :type query: str
148+ :param variables: GraphQL query variables, defaults to {}
149+ :type variables: dict, optional
150+ :return: returns the response json content
151+ :rtype: Any
152+ """
153+
123154 query_var = {}
124155 files_vars = []
125156 # Implementation of spec https://github.com/jaydenseric/graphql-multipart-request-spec
@@ -210,7 +241,7 @@ def query(self, query, variables={}):
210241 verify = self .ssl_verify ,
211242 )
212243 # Build response
213- if r .status_code == requests . codes . ok :
244+ if r .status_code == 200 :
214245 result = r .json ()
215246 if "errors" in result :
216247 logging .error (result ["errors" ][0 ]["message" ])
@@ -220,12 +251,30 @@ def query(self, query, variables={}):
220251 logging .info (r .text )
221252
222253 def fetch_opencti_file (self , fetch_uri , binary = False ):
254+ """get file from the OpenCTI API
255+
256+ :param fetch_uri: download URI to use
257+ :type fetch_uri: str
258+ :param binary: [description], defaults to False
259+ :type binary: bool, optional
260+ :return: returns either the file content as text or bytes based on `binary`
261+ :rtype: str or bytes
262+ """
263+
223264 r = requests .get (fetch_uri , headers = self .request_headers )
224265 if binary :
225266 return r .content
226267 return r .text
227268
228269 def log (self , level , message ):
270+ """log a message with defined log level
271+
272+ :param level: must be a valid logging log level (debug, info, warning, error)
273+ :type level: str
274+ :param message: the message to log
275+ :type message: str
276+ """
277+
229278 if level == "debug" :
230279 logging .debug (message )
231280 elif level == "info" :
@@ -236,6 +285,11 @@ def log(self, level, message):
236285 logging .error (message )
237286
238287 def health_check (self ):
288+ """submit an example request to the OpenCTI API.
289+
290+ :return: returns `True` if the health check has been successful
291+ :rtype: bool
292+ """
239293 try :
240294 test = self .threat_actor .list (first = 1 )
241295 if test is not None :
@@ -245,6 +299,12 @@ def health_check(self):
245299 return False
246300
247301 def get_logs_worker_config (self ):
302+ """get the logsWorkerConfig
303+
304+ return: the logsWorkerConfig
305+ rtype: dict
306+ """
307+
248308 logging .info ("Getting logs worker config..." )
249309 query = """
250310 query LogsWorkerConfig {
@@ -259,6 +319,14 @@ def get_logs_worker_config(self):
259319 return result ["data" ]["logsWorkerConfig" ]
260320
261321 def not_empty (self , value ):
322+ """check if a value is empty for str, list and int
323+
324+ :param value: value to check
325+ :type value: str or list or int
326+ :return: returns `True` if the value is one of the supported types and not empty
327+ :rtype: bool
328+ """
329+
262330 if value is not None :
263331 if isinstance (value , str ):
264332 if len (value ) > 0 :
@@ -279,6 +347,16 @@ def not_empty(self, value):
279347 return False
280348
281349 def process_multiple (self , data , with_pagination = False ):
350+ """processes data returned by the OpenCTI API with multiple entities
351+
352+ :param data: data to process
353+ :type data:
354+ :param with_pagination: whether to use pagination with the API, defaults to False
355+ :type with_pagination: bool, optional
356+ :return: returns either a dict or list with the processes entities
357+ :rtype: list or dict
358+ """
359+
282360 if with_pagination :
283361 result = {"entities" : [], "pagination" : {}}
284362 else :
@@ -306,6 +384,14 @@ def process_multiple(self, data, with_pagination=False):
306384 return result
307385
308386 def process_multiple_ids (self , data ):
387+ """processes data returned by the OpenCTI API with multiple ids
388+
389+ :param data: data to process
390+ :type data:
391+ :return: returns a list of ids
392+ :rtype: list
393+ """
394+
309395 result = []
310396 if data is None :
311397 return result
@@ -316,6 +402,14 @@ def process_multiple_ids(self, data):
316402 return result
317403
318404 def process_multiple_fields (self , data ):
405+ """processes data returned by the OpenCTI API with multiple fields
406+
407+ :param data: data to process
408+ :type data: dict
409+ :return: returns the data dict with all fields processed
410+ :rtype: dict
411+ """
412+
319413 if data is None :
320414 return data
321415 if (
@@ -386,6 +480,13 @@ def process_multiple_fields(self, data):
386480 return data
387481
388482 def upload_file (self , ** kwargs ):
483+ """upload a file to OpenCTI API
484+
485+ :param `**kwargs`: arguments for file upload (required: `file_name` and `data`)
486+ :return: returns the query respons for the file upload
487+ :rtype: dict
488+ """
489+
389490 file_name = kwargs .get ("file_name" , None )
390491 data = kwargs .get ("data" , None )
391492 mime_type = kwargs .get ("mime_type" , "text/plain" )
@@ -462,6 +563,18 @@ def create_vulnerability_if_not_exists(
462563 )
463564
464565 def resolve_role (self , relation_type , from_type , to_type ):
566+ """resolves the role for a specified entity
567+
568+ :param relation_type: input relation type
569+ :type relation_type: str
570+ :param from_type: entity type
571+ :type from_type: str
572+ :param to_type: entity type
573+ :type to_type: str
574+ :return: returns the role mapping
575+ :rtype: dict
576+ """
577+
465578 if from_type == "stix-relation" :
466579 from_type = "stix_relation"
467580 if to_type == "stix-relation" :
0 commit comments