improve logger

This commit is contained in:
ziirish 2015-09-17 10:12:41 +02:00
parent 98daf70198
commit c763d12306
9 changed files with 130 additions and 63 deletions

View file

@ -14,7 +14,8 @@ if __name__ == '__main__':
Main function
"""
parser = OptionParser()
parser.add_option('-v', '--verbose', dest='log', help='verbose output', action='store_true')
parser.add_option('-v', '--verbose', dest='log', help='increase output verbosity (e.g., -vv is more than -v)', action='count')
parser.add_option('-l', '--logfile', dest='logfile', help='where to store logs', metavar='LOGFILE')
parser.add_option('-c', '--config', dest='config', help='configuration file', metavar='CONFIG')
(options, args) = parser.parse_args()
@ -42,5 +43,5 @@ if __name__ == '__main__':
conf = p
break
agent = Agent(conf, options.log)
agent = Agent(conf, options.log, options.logfile)
agent.run()

View file

@ -6,13 +6,18 @@ try:
import ujson as json
except ImportError:
import json
import re
import time
import sys
import logging
import pickle
import traceback
import ConfigParser
import SocketServer
from threading import Thread
from logging import Formatter, StreamHandler
from logging.handlers import RotatingFileHandler
from burpui.misc.utils import BUIlogging
g_port = '10000'
g_bind = '::'
@ -24,13 +29,35 @@ g_timeout = '5'
g_password = 'password'
class BUIAgent:
def __init__(self, conf=None, debug=False):
class BUIAgent(BUIlogging):
def __init__(self, conf=None, debug=False, logfile=None):
global g_port, g_bind, g_ssl, g_version, g_sslcert, g_sslkey, g_password
self.conf = conf
self.dbg = debug
print 'conf: ' + self.conf
print 'debug: ' + str(self.dbg)
self.logger = None
if debug > logging.NOTSET:
levels = [0, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
if debug >= len(levels):
debug = len(levels) - 1
lvl = levels[debug]
self.logger = logging.getLogger(__name__)
self.logger.setLevel(lvl)
if logfile:
handler = RotatingFileHandler(logfile, maxBytes=1024 * 1024 * 100, backupCount=20)
LOG_FORMAT = '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
else:
handler = StreamHandler()
LOG_FORMAT = (
'-' * 80 + '\n' +
'%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
'%(message)s\n' +
'-' * 80
)
handler.setLevel(lvl)
handler.setFormatter(Formatter(LOG_FORMAT))
self.logger.addHandler(handler)
self.logger.info('conf: ' + self.conf)
self.logger.info('level: ' + logging.getLevelName(lvl))
if not conf:
raise IOError('No configuration file found')
@ -42,18 +69,18 @@ class BUIAgent:
with open(conf) as fp:
config.readfp(fp)
try:
self.port = config.getint('Global', 'port')
self.bind = config.get('Global', 'bind')
self.vers = config.getint('Global', 'version')
self.timeout = config.getint('Global', 'timeout')
self.port = self._safe_config_get(config.getint, 'port', cast=int)
self.bind = self._safe_config_get(config.get, 'bind')
self.vers = self._safe_config_get(config.getint, 'version', cast=int)
self.timeout = self._safe_config_get(config.getint, 'timeout', cast=int)
try:
self.ssl = config.getboolean('Global', 'ssl')
except ValueError:
self.app.logger.error("Wrong value for 'ssl' key! Assuming 'false'")
self.ssl = False
self.sslcert = config.get('Global', 'sslcert')
self.sslkey = config.get('Global', 'sslkey')
self.password = config.get('Global', 'password')
self.sslcert = self._safe_config_get(config.get, 'sslcert')
self.sslkey = self._safe_config_get(config.get, 'sslkey')
self.password = self._safe_config_get(config.get, 'password')
except ConfigParser.NoOptionError, e:
raise e
@ -62,6 +89,7 @@ class BUIAgent:
mod = __import__(module, fromlist=['Burp'])
Client = mod.Burp
self.backend = Client(conf=conf)
self.backend.set_logger(self.logger)
except Exception, e:
traceback.print_exc()
self.debug('Failed loading backend for Burp version %d: %s', self.vers, str(e))
@ -89,22 +117,55 @@ class BUIAgent:
self.server = AgentServer((self.bind, self.port), AgentTCPHandler, self)
def _safe_config_get(self, callback, key, sect='Global', cast=None):
"""
:func:`burpui.agent._safe_config_get` is a wrapper to handle
Exceptions throwed by :mod:`ConfigParser`.
:param callback: Function to wrap
:type callback: callable
:param key: Key to retrieve
:type key: str
:param sect: Section of the config file to read
:type sect: str
:param cast: Cast the returned value if provided
:type case: callable
:returns: The value returned by the `callback`
"""
try:
return callback(sect, key)
except ConfigParser.NoOptionError as e:
self._logger('error', str(e))
except ConfigParser.NoSectionError as e:
self._logger('warning', str(e))
if key in self.defaults:
if cast:
return cast(self.defaults[key])
return self.defaults[key]
return None
def run(self):
try:
self.server.serve_forever()
except KeyboardInterrupt:
sys.exit(0)
def debug(self, msg, *args):
if self.dbg:
print msg % (args)
def _logger(self, level, message):
# hide password from logs
msg = message
if self.logger.getEffectiveLevel() != logging.DEBUG:
msg = re.sub(r'"password": \S+', '"password": "*****",', message)
super(BUIAgent, self)._logger(level, msg)
class AgentTCPHandler(SocketServer.BaseRequestHandler):
"One instance per connection. Override handle(self) to customize action."
def handle(self):
# self.request is the client connection
self.server.agent.debug('===============>')
timeout = self.server.agent.timeout
try:
err = None
@ -114,19 +175,17 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
lengthbuf = self.request.recv(8)
length, = struct.unpack('!Q', lengthbuf)
data = self.recvall(length)
self.server.agent.debug('####################')
self.server.agent.debug('recv: %s', data)
self.server.agent.debug('####################')
self.server.agent._logger('info','recv: {}'.format(data))
j = json.loads(data)
_, w, _ = select.select([], [self.request], [], timeout)
if not w:
raise Exception('Socket timed-out 2')
if j['password'] != self.server.agent.password:
self.server.agent.debug('-----> Wrong Password <-----')
self.server.agent._logger('warning', '-----> Wrong Password <-----')
self.request.sendall('KO')
return
if j['func'] not in self.server.agent.methods:
self.server.agent.debug('-----> Wrong method <-----')
self.server.agent._logger('warning', '-----> Wrong method <-----')
self.request.sendall('KO')
return
self.request.sendall('OK')
@ -140,9 +199,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
res = json.dumps(self.server.agent.methods[j['func']](**j['args']))
else:
res = json.dumps(self.server.agent.methods[j['func']]())
self.server.agent.debug('####################')
self.server.agent.debug('result: %s', res)
self.server.agent.debug('####################')
self.server.agent._logger('info', 'result: {}'.format(res))
_, w, _ = select.select([], [self.request], [], timeout)
if not w:
raise Exception('Socket timed-out 3')
@ -159,7 +216,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
with open(res, 'rb') as f:
buf = f.read(1024)
while buf:
self.server.agent.debug('sending %d Bytes', len(buf))
self.server.agent._logger('info', 'sending {} Bytes'.format(len(buf)))
self.request.sendall(buf)
buf = f.read(1024)
_, w, _ = select.select([], [self.request], [], timeout)
@ -171,9 +228,7 @@ class AgentTCPHandler(SocketServer.BaseRequestHandler):
self.request.sendall(res)
self.request.close()
except Exception as e:
self.server.agent.debug('ERROR: %s', str(e))
finally:
self.server.agent.debug('<===============')
self.server.agent._logger('error', '{}'.format(str(e)))
def recvall(self, length=1024):
buf = b''

View file

@ -102,10 +102,12 @@ class Burp(BUIbackend, BUIlogging):
if dummy:
return
self.app = None
self.logger = None
self.acl_handler = False
if server:
if hasattr(server, 'app'):
self.app = server.app
self.set_logger(self.app.logger)
self.acl_handler = server.acl_handler
self.host = g_burphost
self.port = int(g_burpport)

View file

@ -46,10 +46,12 @@ class Burp(Burp1):
global g_burpbin, g_stripbin, g_burpconfcli, g_burpconfsrv, g_tmpdir, BURP_MINIMAL_VERSION
self.proc = None
self.app = None
self.logger = None
self.acl_handler = False
if server:
if hasattr(server, 'app'):
self.app = server.app
self.set_logger(self.app.logger)
self.acl_handler = server.acl_handler
self.burpbin = g_burpbin
self.stripbin = g_stripbin

View file

@ -7,6 +7,9 @@ class BUIbackend:
self.host = host
self.port = port
def set_logger(self, logger):
self.logger = logger
def status(self, query='\n', agent=None):
raise NotImplementedError("Sorry, the current Backend does not implement this method!")

View file

@ -17,16 +17,19 @@ except ImportError:
import configparser as ConfigParser
from burpui.misc.backend.interface import BUIbackend, BUIserverException
from burpui.misc.utils import BUIlogging
class Burp(BUIbackend):
class Burp(BUIbackend,BUIlogging):
def __init__(self, server=None, conf=None):
self.app = None
self.logger = None
self.acl_handler = False
if server:
if hasattr(server, 'app'):
self.app = server.app
self.set_logger(self.app.logger)
self.acl_handler = server.acl_handler
self.servers = {}
self.app.config['SERVERS'] = []

View file

@ -5,6 +5,9 @@ class BUIparser(object):
def __init__(self, app=None, conf=None):
self.app = app
self.conf = conf
self.logger = None
if self.app:
self.logger = self.app.logger
def read_server_conf(self):
raise NotImplementedError("Sorry, the current Parser does not implement this method!")

View file

@ -11,22 +11,24 @@ import sys
import inspect
import zipfile
import tarfile
import logging
if sys.version_info >= (3, 0):
long = int
class human_readable(long):
""" define a human_readable class to allow custom formatting
format specifiers supported :
em : formats the size as bits in IEC format i.e. 1024 bits (128 bytes) = 1Kib
eM : formats the size as Bytes in IEC format i.e. 1024 bytes = 1KiB
sm : formats the size as bits in SI format i.e. 1000 bits = 1kb
sM : formats the size as bytes in SI format i.e. 1000 bytes = 1KB
cm : format the size as bit in the common format i.e. 1024 bits (128 bytes) = 1Kb
cM : format the size as bytes in the common format i.e. 1024 bytes = 1KB
"""
define a human_readable class to allow custom formatting
format specifiers supported :
em : formats the size as bits in IEC format i.e. 1024 bits (128 bytes) = 1Kib
eM : formats the size as Bytes in IEC format i.e. 1024 bytes = 1KiB
sm : formats the size as bits in SI format i.e. 1000 bits = 1kb
sM : formats the size as bytes in SI format i.e. 1000 bytes = 1KB
cm : format the size as bit in the common format i.e. 1024 bits (128 bytes) = 1Kb
cM : format the size as bytes in the common format i.e. 1024 bytes = 1KB
code from: http://code.activestate.com/recipes/578323-human-readable-filememory-sizes-v2/
code from: http://code.activestate.com/recipes/578323-human-readable-filememory-sizes-v2/
"""
def __format__(self, fmt):
# is it an empty format or not a special format for the size class
@ -73,31 +75,24 @@ def currentframe():
class BUIlogging(object):
def _logger(self, level, *args):
if self.app:
logs = {
'info': self.app.logger.info,
'error': self.app.logger.error,
'debug': self.app.logger.debug,
'warning': self.app.logger.warning
}
if level in logs:
if self.logger:
"""
Try to guess where was call the function
"""
cf = currentframe()
(frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(cf)[1]
if cf is not None:
cf = cf.f_back
"""
Try to guess where was call the function
Ugly hack to reformat the message
"""
cf = currentframe()
(frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(cf)[1]
if cf is not None:
cf = cf.f_back
"""
Ugly hack to reformat the message
"""
ar = list(args)
if isinstance(ar[0], str):
ar[0] = filename + ':' + str(cf.f_lineno) + ' => ' + ar[0]
else:
ar = [filename + ':' + str(cf.f_lineno) + ' => {0}'.format(ar)]
args = tuple(ar)
logs[level](*args)
ar = list(args)
if isinstance(ar[0], str):
ar[0] = filename + ':' + str(cf.f_lineno) + ' => ' + ar[0]
else:
ar = [filename + ':' + str(cf.f_lineno) + ' => {0}'.format(ar)]
args = tuple(ar)
self.logger.log(logging.getLevelName(level.upper()), *args)
class BUIcompress():

View file

@ -90,10 +90,13 @@ class BUIServer:
traceback.print_exc()
self.app.logger.error('Import Exception, module \'{0}\': {1}'.format(self.auth, str(e)))
sys.exit(1)
self.acl_engine = self._safe_config_get(config.get, 'acl')
else:
# I know that's ugly, but hey, I need it!
self.app.login_manager._login_disabled = True
self.acl_engine = self._safe_config_get(config.get, 'acl')
# No login => no ACL
self.acl_engine = 'none'
if self.acl_engine and self.acl_engine.lower() != 'none':
try:
mod = __import__(