aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.py42
-rwxr-xr-xmain.py33
-rw-r--r--report.py40
-rw-r--r--requirements.txt6
4 files changed, 51 insertions, 70 deletions
diff --git a/config.py b/config.py
index 10e938f..117c016 100644
--- a/config.py
+++ b/config.py
@@ -1,20 +1,17 @@
# -*- coding: utf-8 -*-
import json
import os
+import sys
class Config(object):
def __init__(self):
self.config = dict()
- self.valid_config = bool
# filepath of the config.json in the project directory
self.path = os.path.dirname(__file__)
self.filepath = ('/'.join([self.path, 'config.json']))
- # load config
- self.load()
-
def load(self):
try:
# try to read config.json
@@ -24,41 +21,12 @@ class Config(object):
except FileNotFoundError:
# if file is absent create file
open(self.filepath, "w").close()
+ print("-- config.json is missing.", file=sys.stderr)
+ print("-- {file} has been created.".format(file=self.filepath), file=sys.stderr)
except json.decoder.JSONDecodeError:
# config file is present but empty
+ print("-- JSON parsing error, please check your config.json file.", file=sys.stderr)
pass
- def get_at(self, attrib: str):
- """
- retrieve attribute from config file
- :param attrib: keyword corresponding to keyword in config dictionary
- :return: value of specified keyword or False if keyword is not present in dictionary
- """
- if attrib in self.config:
- # return corresponding attrib from config
- return self.config[attrib]
- else:
- # if attrib is not present in config return False
- self.config[attrib] = False
-
- def set_at(self, attrib: str, param):
- """
- set attribute to parameter inside config file
- :param attrib: keyword which should be updated/created in config dictionary
- :param param: parameter the keyword should be updated to
- """
- self.config[attrib] = param
-
- # save new attrib to file
- with open(self.filepath, "w", encoding="utf-8") as f:
- f.write(json.dumps(self.config, indent=4))
-
- def unset_at(self, attrib: str):
- """
- unset attribute inside config file
- :param attrib: attribute which should be unset inside config file
- """
- if attrib in self.config:
- # only if attrib is actually present unset it
- self.config.pop(attrib)
+ return self.config
diff --git a/main.py b/main.py
index 3cec813..60e65f5 100755
--- a/main.py
+++ b/main.py
@@ -11,7 +11,6 @@ import sys
import tabulate
from defusedxml import ElementTree
-from config import Config
from report import ReportDomain
@@ -25,9 +24,9 @@ class AbuseReport:
self.start = arguments.start
self.stop = arguments.stop
self.path = os.path.dirname(__file__)
- self.config = Config()
self.conn = sqlite3.connect("/".join([self.path, "spam.db"]))
+ self.Report = ReportDomain(self.conn)
self.jid_pattern = re.compile("^(?:([^\"&'/:<>@]{1,1023})@)?([^/@]{1,1023})(?:/(.{1,1023}))?$")
self.message_pattern = re.compile(r'<message.*?</message>', re.DOTALL)
@@ -79,6 +78,9 @@ class AbuseReport:
# set stop value to now
self.stop = dt.datetime.strftime(dt.datetime.now(), '%Y-%m-%dT%H:%M:%S')
+ # add validated timestamps to report class
+ self.Report.addtime(self.start, self.stop)
+
# if one or more domains are specified return only their info
if self.domain is not None:
@@ -86,11 +88,10 @@ class AbuseReport:
for domain in self.domain:
# build and execute
- sql = '''SELECT COUNT(*) AS messages, COUNT(DISTINCT user) AS bots, domain, MIN(ts) AS first, MAX(ts) AS last \
- FROM spam \
- WHERE domain = :domain \
- AND ts > :start \
- AND ts < :stop;'''
+ sql = '''SELECT COUNT(*) AS messages, COUNT(DISTINCT user) AS bots, domain, MIN(ts) AS first, MAX(ts) AS last
+ FROM spam
+ WHERE domain = :domain
+ AND ts > :start AND ts < :stop;'''
parameter = {
"domain": domain,
"start": self.start,
@@ -114,15 +115,14 @@ class AbuseReport:
else:
# build and execute
- sql = '''SELECT COUNT(*) AS messages, COUNT(DISTINCT user) AS bots, domain AS domain from spam \
- WHERE ts > :start \
- AND ts < :stop \
+ sql = '''SELECT COUNT(*) AS messages, COUNT(DISTINCT user) AS bots, domain AS domain from spam
+ WHERE ts > :start AND ts < :stop
GROUP BY domain ORDER BY 1 DESC LIMIT 10;'''
result = self.conn.execute(sql, {"start": self.start, "stop": self.stop}).fetchall()
# tabelize data
- spam_table = tabulate.tabulate(result, headers=["messages", "bots", "domain", "first seen", "last seen"],
- tablefmt="github")
+ spam_table = tabulate.tabulate(result, tablefmt="psql", headers=["messages", "bots", "domain","first seen",
+ "last seen"])
# output to stdout
output = "\n\n".join([spam_table])
@@ -203,9 +203,6 @@ class AbuseReport:
:param domain: string containing a domain name
:param query: list of tuples containing the query results for the specified domain/s
"""
- # init report class
- report = ReportDomain(self.config, self.conn)
-
try:
# open abuse report template file
with open("/".join([self.path, "template/abuse-template.txt"]), "r", encoding="utf-8") as template:
@@ -225,15 +222,15 @@ class AbuseReport:
# write report files
with open("/".join([self.path, "report", report_filename]), "w", encoding="utf-8") as report_out:
- content = report.template(report_template, domain, query)
+ content = self.Report.template(report_template, domain, query)
report_out.write(content)
with open("/".join([self.path, "report", jids_filename]), "w", encoding="utf-8") as report_out:
- content = report.jids(domain)
+ content = self.Report.jids(domain)
report_out.write(content)
with open("/".join([self.path, "report", logs_filename]), "w", encoding="utf-8") as report_out:
- content = report.logs(domain)
+ content = self.Report.logs(domain)
report_out.write(content)
diff --git a/report.py b/report.py
index e87517c..98d50a9 100644
--- a/report.py
+++ b/report.py
@@ -2,15 +2,23 @@
import dns.resolver as dns
import tabulate
+from config import Config
+
class ReportDomain:
- def __init__(self, config, conn):
+ def __init__(self, conn):
"""
- :param config: configuration object
:param conn: sqlite connection object
"""
- self.config = config
+ self.config = Config().load()
self.conn = conn
+ self.start = int()
+ self.stop = int()
+
+ def addtime(self, start, stop):
+ # add start and stop timestamps
+ self.start = start
+ self.stop = stop
def template(self, template: str, domain: str, query: list):
"""
@@ -20,7 +28,7 @@ class ReportDomain:
:param query: list of tuples containing the query results for the specified domain/s
:return: string containing the fully formatted abuse report
"""
- name = self.config.get_at("name")
+ name = self.config["name"]
# lookup and format srv target and ip
srv, ips = self.srv(domain)
@@ -37,9 +45,13 @@ class ReportDomain:
:param domain: string containing a domain name
:return: formatted result string
"""
-
- jids = self.conn.execute('''SELECT user || '@' || domain AS jid FROM spam WHERE ts BETWEEN DATE('now','-14 days')
- AND DATE('now') AND domain=:domain GROUP BY user ORDER BY 1;''', {"domain": domain}).fetchall()
+ sql = '''SELECT user || '@' || domain AS jid FROM spam
+ WHERE ts > :start
+ AND ts < :stop
+ AND domain = :domain
+ GROUP BY user ORDER BY 1;'''
+ param = {"domain": domain, "start": self.start, "stop":self.stop}
+ jids = self.conn.execute(sql, param).fetchall()
return tabulate.tabulate(jids, tablefmt="plain")
@@ -49,11 +61,15 @@ class ReportDomain:
:param domain: string containing a domain name
:return: formatted string containing the result
"""
- logs = self.conn.execute('''SELECT CHAR(10) || MIN(ts) || ' - ' || MAX(ts) || char(10) || COUNT(*) ||
- 'messages:' || char(10) ||'========================================================================' ||
- char(10) || message || char(10) || '========================================================================'
- FROM spam WHERE ts BETWEEN DATE('now','-14 days') AND DATE('now') AND domain=:domain GROUP BY message ORDER
- BY COUNT(*) DESC LIMIT 10;''', {"domain": domain}).fetchall()
+ sql = '''SELECT CHAR(10) || MIN(ts) || ' - ' || MAX(ts) || char(10) || COUNT(*) || 'messages:' || char(10) ||
+ '========================================================================' || char(10) || message ||
+ char(10) || '========================================================================' FROM spam
+ WHERE ts > :start
+ AND ts < :stop
+ AND domain = :domain
+ GROUP BY message ORDER BY COUNT(*) DESC LIMIT 10;'''
+ param = {"domain": domain, "start": self.start, "stop": self.stop}
+ logs = self.conn.execute(sql, param).fetchall()
return tabulate.tabulate(logs, tablefmt="plain")
diff --git a/requirements.txt b/requirements.txt
index 785dc6e..8dec4df 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
-defusedxml
-tabulate
-dnspython
+defusedxml>=0.6.0
+dnspython>=1.16.0
+tabulate>=0.8.5 \ No newline at end of file