Skip to content

Commit

Permalink
#316 Refactor cortexutils to add a Responder class
Browse files Browse the repository at this point in the history
  • Loading branch information
nadouani committed Jul 30, 2018
1 parent fdb7746 commit bdf1c01
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 139 deletions.
166 changes: 29 additions & 137 deletions contrib/cortexutils/analyzer.py
Original file line number Diff line number Diff line change
@@ -1,120 +1,22 @@
#!/usr/bin/env python
# encoding: utf-8
import os
import sys
import codecs

import json
from cortexutils.worker import Worker
from cortexutils.extractor import Extractor


class Analyzer:
class Analyzer(Worker):

def __init__(self):
self.__set_encoding()

# Prepare in/out/err streams
self.fperror = sys.stderr
self.fpinput = sys.stdin
self.fpoutput = sys.stdout

# Load input
self.__input = json.load(self.fpinput)

# Set parameters
self.data_type = self.get_param('dataType', None, 'Missing dataType field')
self.tlp = self.get_param('tlp', 2)

self.enable_check_tlp = self.get_param('config.check_tlp', False)
self.max_tlp = self.get_param('config.max_tlp', 2)

# Set proxy configuration if available
self.http_proxy = self.get_param('config.proxy.http')
self.https_proxy = self.get_param('config.proxy.https')

self.__set_proxies()

# Finally run check tlp
if not (self.__check_tlp()):
self.error('TLP is higher than allowed.')
Worker.__init__(self)

# Not breaking compatibility
self.artifact = self.__input
self.artifact = self._input

# Check for auto extraction config
self.auto_extract = self.get_param('config.auto_extract', self.get_param('config.auto_extract_artifacts', True))

# Not breaking compatibility
def notSupported(self):
self.error('This datatype is not supported by this analyzer.')

# Not breaking compatibility
def unexpectedError(self, e):
self.error('Unexpected Error: ' + str(e))

# Not breaking compatibility
def getData(self):
"""For not breaking compatibility to cortexutils.analyzer, this wraps get_data()"""
return self.get_data()

# Not breaking compatibility
def getParam(self, name, default=None, message=None):
"""For not breaking compatibility to cortexutils.analyzer, this wraps get_param()"""
return self.get_param(name=name, default=default, message=message)

# Not breaking compatibility
def checkTlp(self, message):
if not (self.__check_tlp()):
self.error(message)

def __set_proxies(self):
if self.http_proxy is not None:
os.environ['http_proxy'] = self.http_proxy
if self.https_proxy is not None:
os.environ['https_proxy'] = self.https_proxy

def __set_encoding(self):
try:
if sys.stdout.encoding != 'UTF-8':
if sys.version_info[0] == 3:
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
else:
sys.stdout = codecs.getwriter('utf-8')(sys.stdout, 'strict')
if sys.stderr.encoding != 'UTF-8':
if sys.version_info[0] == 3:
sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
else:
sys.stderr = codecs.getwriter('utf-8')(sys.stderr, 'strict')
except Exception:
pass

def __get_param(self, source, name, default=None, message=None):
"""Extract a specific parameter from given source.
:param source: Python dict to search through
:param name: Name of the parameter to get. JSON-like syntax, e.g. `config.username` at first, but in recursive
calls a list
:param default: Default value, if not found. Default: None
:param message: Error message. If given and name not found, exit with error. Default: None"""

if isinstance(name, str):
name = name.split('.')

if len(name) == 0:
# The name is empty, return the source content
return source
else:
new_source = source.get(name[0])
if new_source is not None:
return self.__get_param(new_source, name[1:], default, message)
else:
if message is not None:
self.error(message)
return default

def __check_tlp(self):
"""Check if tlp is okay or not; returns False if too high."""

return not (self.enable_check_tlp and self.tlp > self.max_tlp)

def get_data(self):
"""Wrapper for getting data from input dict.
Expand All @@ -123,14 +25,6 @@ def get_data(self):
return self.get_param('filename', None, 'Missing filename.')
return self.get_param('data', None, 'Missing data field')

def get_param(self, name, default=None, message=None):
"""Just a wrapper for Analyzer.__get_param.
:param name: Name of the parameter to get. JSON-like syntax, e.g. `config.username`
:param default: Default value, if not found. Default: None
:param message: Error message. If given and name not found, exit with error. Default: None"""

return self.__get_param(self.__input, name, default, message)

def build_taxonomy(self, level, namespace, predicate, value):
"""
:param level: info, safe, suspicious or malicious
Expand All @@ -147,7 +41,7 @@ def build_taxonomy(self, level, namespace, predicate, value):
'namespace': namespace,
'predicate': predicate,
'value': value
}
}

def summary(self, raw):
"""Returns a summary, needed for 'short.html' template. Overwrite it for your needs!
Expand All @@ -164,31 +58,6 @@ def artifacts(self, raw):
# Return empty list
return []

def error(self, message, ensure_ascii=False):
"""Stop analyzer with an error message. Changing ensure_ascii can be helpful when stucking
with ascii <-> utf-8 issues. Additionally, the input as returned, too. Maybe helpful when dealing with errors.
:param message: Error message
:param ensure_ascii: Force ascii output. Default: False"""

analyzerInput = self.__input
if 'password' in analyzerInput.get('config', {}):
analyzerInput['config']['password'] = 'REMOVED'
if 'key' in analyzerInput.get('config', {}):
analyzerInput['config']['key'] = 'REMOVED'
if 'apikey' in analyzerInput.get('config', {}):
analyzerInput['config']['apikey'] = 'REMOVED'
if 'api_key' in analyzerInput.get('config', {}):
analyzerInput['config']['api_key'] = 'REMOVED'

json.dump({'success': False,
'input': analyzerInput,
'errorMessage': message},
self.fpoutput,
ensure_ascii=ensure_ascii)

# Force exit after error
sys.exit(1)

def report(self, full_report, ensure_ascii=False):
"""Returns a json dict via stdout.
Expand All @@ -212,3 +81,26 @@ def report(self, full_report, ensure_ascii=False):
def run(self):
"""Overwritten by analyzers"""
pass

# Not breaking compatibility
def notSupported(self):
self.error('This datatype is not supported by this analyzer.')

# Not breaking compatibility
def unexpectedError(self, e):
self.error('Unexpected Error: ' + str(e))

# Not breaking compatibility
def getData(self):
"""For not breaking compatibility to cortexutils.analyzer, this wraps get_data()"""
return self.get_data()

# Not breaking compatibility
def getParam(self, name, default=None, message=None):
"""For not breaking compatibility to cortexutils.analyzer, this wraps get_param()"""
return self.get_param(name=name, default=default, message=message)

# Not breaking compatibility
def checkTlp(self, message):
if not (self.__check_tlp()):
self.error(message)
56 changes: 56 additions & 0 deletions contrib/cortexutils/responder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python
# encoding: utf-8

import json
from cortexutils.worker import Worker


class Responder(Worker):

def __init__(self):
Worker.__init__(self)

# Not breaking compatibility
self.artifact = self._input

def get_data(self):
"""Wrapper for getting data from input dict.
:return: Data (observable value) given through Cortex"""
return self.get_param('data', None, 'Missing data field')

def build_operation(self, op_type, parameters={}):
"""
:param op_type: an operation type as a string
:param parameters: a dict including the operation's params
:return: dict
"""
operation = {
'type': op_type
}
operation.update(parameters)

return operation

def operations(self, raw):
"""Returns the list of operations to be executed after the job completes
:returns: by default return an empty array"""
return []

def report(self, full_report, ensure_ascii=False):
"""Returns a json dict via stdout.
:param full_report: Responsder results as dict.
:param ensure_ascii: Force ascii output. Default: False"""

report = {
'success': True,
'full': full_report,
'operations':
}
json.dump(report, self.fpoutput, ensure_ascii=ensure_ascii)

def run(self):
"""Overwritten by responders"""
pass
Loading

0 comments on commit bdf1c01

Please sign in to comment.