From 749419533d5292edb99c1fddde0d819b1e7f8493 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20K=C3=A1cha?= <ph@cesnet.cz>
Date: Thu, 26 Feb 2015 15:08:24 +0100
Subject: [PATCH] * warden_filer: now able to filter outgoing messages in
 sender mode * filenames now have .idea extension (Mentat requires that :) ) *
 bugfix: use list range instead of generator xrange on event index list
 generation (also in warden_client)

---
 warden3/contrib/warden_filer/README           |  3 ++
 warden3/contrib/warden_filer/warden_filer.cfg | 11 ++++-
 warden3/contrib/warden_filer/warden_filer.py  | 49 +++++++++++++++++--
 warden3/warden_client/warden_client.py        |  2 +-
 4 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/warden3/contrib/warden_filer/README b/warden3/contrib/warden_filer/README
index 12f500a..a33de1f 100644
--- a/warden3/contrib/warden_filer/README
+++ b/warden3/contrib/warden_filer/README
@@ -71,6 +71,9 @@ JSON object, containing configuration. See also warden_filer.cfg as example.
    sender - configuration section for sender mode
       dir - directory, whose "incoming" subdir will be checked for Idea
             events to send out
+      filter - filter fields (same as in Warden query, see Warden and Idea
+               doc, possible keys: cat, nocat, group, nogroup, tag, notag),
+               unmatched events get discarded and deleted
       node - o information about detector to be prepended into event Node
              array (see Idea doc)
    receiver - configuration section for receiver mode
diff --git a/warden3/contrib/warden_filer/warden_filer.cfg b/warden3/contrib/warden_filer/warden_filer.cfg
index 1c7ee0c..402f518 100644
--- a/warden3/contrib/warden_filer/warden_filer.cfg
+++ b/warden3/contrib/warden_filer/warden_filer.cfg
@@ -15,10 +15,19 @@
         // Maildir like directory, whose "incoming" subdir will be checked
         // for Idea events to send out
         "dir": "warden_sender",
+        // Optional filter fields, unmatched events are discarded (and removed)
+        "filter": {
+            "cat": ["Test", "Recon.Scanning"],
+            "nocat": null,
+            "group": ["cz.example"],
+            "nogroup": null,
+            "tag": null,
+            "notag": ["Honeypot"]
+        },
         // Optional information about detector to be prepended into Idea Node array
         "node": {
             "Name": "cz.example.warden.test",
-            "Type": ["External"],
+            "Type": ["Relay"],
             "SW": ["warden_filer"],
             "AggrWin": "00:05:00",
             "Note": "Test warden_filer sender"
diff --git a/warden3/contrib/warden_filer/warden_filer.py b/warden3/contrib/warden_filer/warden_filer.py
index 84b1c56..b4c19c8 100644
--- a/warden3/contrib/warden_filer/warden_filer.py
+++ b/warden3/contrib/warden_filer/warden_filer.py
@@ -95,7 +95,7 @@ class SafeDir(object):
 
 
     def _get_new_name(self, device=0, inode=0):
-        return "%s.%d.%f.%d.%d" % (
+        return "%s.%d.%f.%d.%d.idea" % (
             self.hostname, self.pid, time.time(), device, inode)
 
 
@@ -177,10 +177,46 @@ def receiver(config, wclient, sdir, oneshot):
 
 
 
+def match_event(event, cat=None, nocat=None, tag=None, notag=None, group=None, nogroup=None):
+
+    cat_match = tag_match = group_match = True
+
+    if cat or nocat:
+        event_cats = event.get("Category")
+        event_full_cats = set(event_cats) | set(cat.split(".", 1)[0] for cat in event_cats)
+        cat_match = set(cat or nocat) & event_full_cats
+        cat_match = not cat_match if nocat else cat_match
+
+    try:
+        event_node = event.get("Node", [])[0]
+    except IndexError:
+        event_node = {}
+
+    if tag or notag:
+        event_tags = set(event_node.get("Type", []))
+        tag_match = set(tag or notag) & event_tags
+        tag_match = not tag_match if notag else tag_match
+
+    if group or nogroup:
+        event_name = event_node.get("Name")
+        namesplit = event_name.split(".")
+        allnames = set([".".join(namesplit[0:l]) for l in range(1, len(namesplit)+1)])
+        group_match = set(group or nogroup) & allnames
+        group_match = not group_match if nogroup else group_match
+
+    return cat_match and tag_match and group_match
+
+
+
 def sender(config, wclient, sdir, oneshot):
     send_events_limit = config.get("send_events_limit", 500)
     poll_time = config.get("poll_time", 5)
     node = config.get("node", None)
+    conf_filt = config.get("filter", {})
+    filt = {}
+    # Extract filter explicitly to be sure we have right param names for match_event
+    for s in ("cat", "nocat", "tag", "notag", "group", "nogroup"):
+        filt[s] = conf_filt.get(s, None)
 
     while running_flag:
         nflist = sdir.get_incoming()
@@ -194,6 +230,7 @@ def sender(config, wclient, sdir, oneshot):
 
         events = []
         nf_sent = []
+        count_ok = count_err = count_unmatched = 0
         for nf in nflist:
             # prepare event array from files
             try:
@@ -204,6 +241,11 @@ def sender(config, wclient, sdir, oneshot):
                 with nf.open("rb") as fd:
                     data = fd.read()
                     event = json.loads(data)
+                    if not match_event(event, **filt):
+                        wclient.logger.debug("Unmatched event: %s" % data)
+                        count_unmatched += 1
+                        nf.remove()
+                        continue
                     if node:
                         nodelist = event.setdefault("Node", [])
                         nodelist.insert(0, node)
@@ -216,11 +258,10 @@ def sender(config, wclient, sdir, oneshot):
 
         res = wclient.sendEvents(events)
 
-        count_ok = count_err = count_retry = 0
         if isinstance(res, Error):
             for e in res.errors:
                 errno = e["error"]
-                evlist = e.get("events", xrange(len(nf_sent)))  # None means all
+                evlist = e.get("events", range(len(nf_sent)))  # None means all
                 for i in evlist:
                     if nf_sent[i]:
                         nf_sent[i].moveto(dest_dir)
@@ -233,7 +274,7 @@ def sender(config, wclient, sdir, oneshot):
                 name.remove()
                 count_ok += 1
         wclient.logger.info(
-            "warden_filer: saved %d, errors %d" % (count_ok, count_err))
+            "warden_filer: saved %d, errors %d, unmatched %d" % (count_ok, count_err, count_unmatched))
 
 
 
diff --git a/warden3/warden_client/warden_client.py b/warden3/warden_client/warden_client.py
index c177682..f1a9b21 100644
--- a/warden3/warden_client/warden_client.py
+++ b/warden3/warden_client/warden_client.py
@@ -525,7 +525,7 @@ class Client(object):
                 res.errors.sort(key=itemgetter("error"))
                 for e in res.errors:
                     errno = e["error"]
-                    evlist = e.get("events", xrange(len(ev)))   # none means all
+                    evlist = e.get("events", range(len(ev)))   # none means all
                     if errno < 500 or not attempt:
                         # Fatal error or last try, translate indices
                         # to original and prepare for returning to caller
-- 
GitLab